Git for Teams: Complete Guide to Push, Pull, Revert, Merge & Team Workflows (with Commands)
Working on a solo project with Git is easy. Working on the same codebase with five other developers, all pushing to the same branches, is where things get messy fast. Merge conflicts, accidental force-pushes, broken main branches, lost commits — every team has horror stories.
This guide is the comprehensive Git reference I wish I’d had when I first joined a team. You’ll learn the essential commands, the team workflows that actually work in production, how to safely undo any mistake, and which tools make collaboration easier. Every command has a real example, and there’s a quick-reference cheat sheet at the end you can bookmark.
Why Git Workflow Matters in Teams
A solo developer can get away with git add ., git commit -m "stuff", and git push forever. A team can’t. Without a workflow:
- Two developers overwrite each other’s work
- The main branch breaks because someone pushed broken code
- Features get tangled together and can’t be released independently
- Reviewing changes becomes impossible
- Rollbacks turn into all-night incidents
A good Git workflow solves all of this through branching, pull requests, and clear conventions that the whole team follows.
The Essential Git Commands Every Developer Must Know
Before getting into team workflows, here are the foundational commands. Master these and 90% of your daily Git work is covered.
1. Cloning and Configuring
# Clone a repository (downloads the project to your machine)
git clone https://github.com/yourcompany/project.git
# Set your identity (only needs to be done once per machine, or per repo)
git config --global user.name "Jane Doe"
git config --global user.email "[email protected]"
# See your current config
git config --list
# Use VS Code as your default Git editor
git config --global core.editor "code --wait"
2. Checking Status and History
# What files have I changed? (run this constantly)
git status
# Show commit history
git log
# Compact one-line history
git log --oneline --graph --all
# Show what changed in your unstaged files
git diff
# Show what's staged for commit
git diff --staged
3. Staging and Committing
# Stage a specific file
git add src/components/Button.jsx
# Stage everything that changed
git add .
# Unstage a file (move back out of staging)
git restore --staged src/components/Button.jsx
# Commit with a message
git commit -m "Add primary variant to Button component"
# Stage and commit all tracked files in one command
git commit -am "Fix typo in user profile page"
# Amend the last commit (add forgotten files, fix message)
# WARNING: Only do this if you haven't pushed yet
git commit --amend -m "Better commit message"
4. Pushing and Pulling
# Push your local commits to the remote
git push
# Push a new branch and set it to track the remote
git push -u origin feature/login-page
# Download remote changes WITHOUT merging them into your branch
git fetch
# Download remote changes AND merge them into your current branch
git pull
# Safer alternative: pull with rebase (keeps history linear)
git pull --rebase
Pro tip:
git fetchfollowed by reviewing the changes is safer thangit pullblindly. Pull is just “fetch + merge”.
5. Branching (The Heart of Team Workflows)
# List all local branches (current branch marked with *)
git branch
# List local + remote branches
git branch -a
# Create a new branch
git branch feature/user-dashboard
# Switch to a branch
git switch feature/user-dashboard
# (Older syntax, still works:) git checkout feature/user-dashboard
# Create AND switch in one command
git switch -c feature/user-dashboard
# (Older:) git checkout -b feature/user-dashboard
# Delete a local branch (after it's merged)
git branch -d feature/user-dashboard
# Force-delete a branch (even if not merged — be careful)
git branch -D feature/user-dashboard
# Delete a remote branch
git push origin --delete feature/user-dashboard
# Rename current branch
git branch -m new-branch-name
6. Merging and Rebasing
# Merge another branch INTO your current branch
git switch main
git merge feature/login-page
# Rebase your branch on top of another (cleaner history)
git switch feature/login-page
git rebase main
# Abort a merge or rebase if it goes wrong
git merge --abort
git rebase --abort
Merge vs rebase in one sentence: Merge preserves history exactly as it happened (with a merge commit); rebase rewrites your commits on top of the target branch as if you started from there (cleaner, but rewrites history).
How to Work with Multiple Developers on Git (Team Workflows)
Here are the three most common branching strategies. Pick one as a team and stick with it.
Workflow 1: GitHub Flow (Simplest — Recommended for Most Teams)
Used by GitHub itself and most modern startups. Just two rules:
mainis always deployable- Everything else happens on feature branches that get merged via Pull Request
Daily flow:
# Start every new feature from latest main
git switch main
git pull
# Create your feature branch
git switch -c feature/add-search-bar
# Work, commit, push regularly
git add .
git commit -m "Add search input UI"
git push -u origin feature/add-search-bar
# Open a Pull Request on GitHub/GitLab/Bitbucket
# Get reviewed, address feedback, merge
# Delete branch after merge
Best for: SaaS apps, small-to-medium teams, continuous deployment.
Workflow 2: Git Flow (For Versioned Releases)
More structured, uses multiple long-lived branches:
main— production code onlydevelop— integration branch for next releasefeature/*— new features (branched off develop)release/*— preparing a release (branched off develop)hotfix/*— emergency production fixes (branched off main)
Best for: Mobile apps, desktop software, anything with versioned releases. Overkill for most web apps in 2026 — most teams have moved to GitHub Flow.
Workflow 3: Trunk-Based Development
Everyone commits directly to main (or to very short-lived branches that merge within hours). Requires strong CI, feature flags, and discipline.
Best for: High-performing teams at scale (Google, Meta, Netflix). Risky without good automation.
The Pull Request (PR) Workflow Step by Step
This is how 90% of professional teams collaborate. Every change goes through a PR before merging.
1. Start from the latest main:
git switch main
git pull
2. Create a feature branch with a descriptive name:
git switch -c feature/JIRA-1234-add-payment-method
Common naming conventions: feature/, bugfix/, hotfix/, chore/ — prefix + ticket number + short description.
3. Make commits with clear messages:
git add .
git commit -m "feat(payments): add Stripe payment method form"
(See Conventional Commits for a popular standard.)
4. Keep your branch up to date with main:
# Option A: merge main into your branch (preserves your commit history)
git switch feature/JIRA-1234-add-payment-method
git fetch
git merge origin/main
# Option B: rebase (cleaner, but rewrites your commits)
git fetch
git rebase origin/main
5. Push and open a PR:
git push -u origin feature/JIRA-1234-add-payment-method
Then go to GitHub/GitLab and open a Pull/Merge Request. Add a clear description of what changed and why, link the ticket, request reviewers.
6. Address review feedback:
git add .
git commit -m "Address review: extract validation to helper"
git push
7. After approval and merge, clean up:
git switch main
git pull
git branch -d feature/JIRA-1234-add-payment-method
How to Resolve Merge Conflicts
Conflicts happen when two people change the same lines. Git can’t decide which version wins, so it asks you.
git merge feature/login-page
# CONFLICT (content): Merge conflict in src/auth.js
Open the conflicted file. You’ll see:
<<<<<<< HEAD
const apiUrl = 'https://api.production.com';
=======
const apiUrl = 'https://api.staging.com';
>>>>>>> feature/login-page
- Everything between
<<<<<<< HEADand=======is YOUR version - Everything between
=======and>>>>>>> feature/login-pageis THE OTHER version
Decide which to keep (or combine both), delete the conflict markers, save the file. Then:
git add src/auth.js
git commit # Git auto-fills a merge commit message
If the conflict is overwhelming and you want to start over:
git merge --abort
VS Code, GitKraken, and similar tools have visual merge editors that show both versions side by side with “Accept Current / Accept Incoming / Accept Both” buttons. Much easier than editing manually.
How to Undo and Revert Changes in Git (Every Scenario)
This is the section that saves careers. Bookmark it.
Undo uncommitted changes (working directory)
# Discard changes to one file (CAREFUL — can't be undone)
git restore src/app.js
# Discard ALL uncommitted changes
git restore .
# Unstage a file (keeps your changes, just removes from staging area)
git restore --staged src/app.js
Undo the last commit (but keep the changes)
# Undo last commit, keep changes in staging
git reset --soft HEAD~1
# Undo last commit, keep changes in working dir (unstaged)
git reset HEAD~1
Completely delete the last commit and its changes
# DESTRUCTIVE — changes are GONE
git reset --hard HEAD~1
Revert a commit that’s already been pushed
Never use git reset --hard on pushed commits. Use git revert instead — it creates a NEW commit that undoes the changes, preserving history.
# Find the commit you want to undo
git log --oneline
# Revert it (creates a new commit)
git revert abc1234
# Push the revert commit
git push
Undo a merge that’s already been pushed
git revert -m 1 <merge-commit-hash>
git push
The -m 1 flag tells Git to revert back to the first parent (the branch you merged into).
Recover a deleted branch or lost commit (Git’s safety net)
# Reflog shows EVERYTHING you've done in Git, including deletes
git reflog
# Find the lost commit/branch (e.g., shows: abc1234 HEAD@{5}: commit: lost work)
# Recover it
git checkout -b recovered-branch abc1234
The reflog has saved more careers than any other Git feature. It keeps a local record of every move HEAD made for 90 days by default, even after you delete a branch.
Undo a force push
If a teammate force-pushed and overwrote your work:
# Look in the reflog for the commit hash before the force-push happened
git reflog
# Restore the branch to that point
git push origin <commit-hash>:branch-name --force-with-lease
Always use --force-with-lease instead of --force — it refuses to push if someone else has pushed in the meantime, preventing you from overwriting their work.
Stashing: Save Work Without Committing
When you need to switch branches but aren’t ready to commit:
# Save your current changes (and clean the working directory)
git stash
# Save with a description
git stash push -m "WIP: refactoring auth middleware"
# See your stashes
git stash list
# Restore the most recent stash (and remove it from the stash list)
git stash pop
# Apply a stash but keep it in the list
git stash apply
# Apply a specific stash
git stash apply stash@{2}
# Delete a stash
git stash drop stash@{0}
# Clear ALL stashes (careful)
git stash clear
Cherry-Picking: Apply One Commit From Another Branch
Useful when you want a single fix from a feature branch without merging everything:
# Find the commit you want
git log feature/login-page --oneline
# Apply it to your current branch
git cherry-pick abc1234
Essential Tools That Make Git in Teams Easier
Hosting platforms
| Tool | Best for |
|---|---|
| GitHub | Open source, most popular, best ecosystem |
| GitLab | Built-in CI/CD, self-hosted option, full DevOps |
| Bitbucket | Teams already using Atlassian (Jira, Confluence) |
| Azure DevOps | Microsoft shops, enterprise |
Visual Git clients (GUI)
| Tool | Notes |
|---|---|
| GitKraken | Beautiful UI, paid for commercial use |
| Sourcetree | Free, by Atlassian, solid for beginners |
| GitHub Desktop | Free, simple, GitHub-focused |
| Fork | Fast, native macOS/Windows, one-time fee |
| Tower | Polished, paid subscription |
Terminal tools
| Tool | What it does |
|---|---|
| Lazygit | Terminal UI for Git — incredibly fast once you learn it |
| tig | Text-mode interface for browsing history |
| gh | GitHub’s official CLI (gh pr create, gh pr review) |
| glab | GitLab equivalent of gh |
| delta | Better-looking git diffs |
IDE integration
- VS Code has excellent built-in Git support plus the GitLens extension (a must-install)
- JetBrains IDEs (PHPStorm, IntelliJ, WebStorm) have the best built-in Git UI of any IDE
- Vim/Neovim users: vim-fugitive is the gold standard
Workflow automation
- Husky — runs scripts on Git hooks (e.g., run linter before every commit)
- lint-staged — only lint files that are staged (faster than linting everything)
- Commitizen — interactive prompt for writing conventional commit messages
- commitlint — rejects commits that don’t follow your team’s message format
- pre-commit — Python-based hook framework, language-agnostic
Code review and PR tools
- Reviewable — better PR review UI than GitHub native
- Graphite — stacked PR workflow for larger teams
- CodeRabbit / Codium AI — AI-assisted PR reviews
Best Practices for Git in Teams
- Pull before you push. Always start a session with
git pullto avoid avoidable conflicts. - Commit early and often. Small commits are easier to review and revert than huge ones.
- Write clear commit messages. “Fix bug” is useless six months from now. Try:
fix(auth): prevent token refresh loop when offline. - Never force-push to shared branches.
main,develop, and any branch with an open PR should be untouchable. - Use
--force-with-leaseinstead of--forcewhen you must force-push your own branch. - Don’t commit secrets. Use
.gitignoreand tools likegit-secretsortruffleHogto scan for accidental leaks. - Keep branches short-lived. Long-running branches accumulate conflicts. Merge within days, not weeks.
- Review your own PR first. Read the diff before requesting reviewers. You’ll catch half your own bugs.
- Protect your main branch. Enable branch protection in GitHub/GitLab: require PR reviews, require CI to pass, block direct pushes.
- Use the reflog. When something goes wrong,
git reflogis your friend.
Git Commands Cheat Sheet (Bookmark This)
Daily essentials
| Command | What it does |
|---|---|
git status |
What’s changed |
git diff |
Show unstaged changes |
git add . |
Stage everything |
git commit -m "msg" |
Commit with message |
git push |
Upload commits |
git pull |
Download + merge |
git fetch |
Download without merging |
git log --oneline --graph |
Pretty history |
Branching
| Command | What it does |
|---|---|
git branch |
List branches |
git switch -c name |
Create + switch to branch |
git switch main |
Switch to main |
git merge branch |
Merge branch into current |
git rebase main |
Rebase onto main |
git branch -d name |
Delete merged branch |
Undoing things
| Command | What it does |
|---|---|
git restore file |
Discard file changes |
git restore --staged file |
Unstage file |
git reset --soft HEAD~1 |
Undo last commit, keep changes |
git reset --hard HEAD~1 |
DELETE last commit (local) |
git revert abc1234 |
Undo a pushed commit safely |
git revert -m 1 hash |
Undo a merge commit |
git reflog |
See everything (recover lost work) |
Stashing
| Command | What it does |
|---|---|
git stash |
Save work-in-progress |
git stash pop |
Restore latest stash |
git stash list |
See all stashes |
git stash apply stash@{2} |
Apply specific stash |
Remote management
| Command | What it does |
|---|---|
git remote -v |
List remotes |
git remote add name url |
Add a remote |
git push -u origin branch |
Push + track remote |
git push origin --delete branch |
Delete remote branch |
Investigation
| Command | What it does |
|---|---|
git blame file |
Who changed each line |
git log -p file |
History of a specific file |
git log --author="name" |
Commits by author |
git bisect start |
Binary search for a bad commit |
Wrapping Up
Git is hard not because the commands are complex but because the consequences of mistakes feel scary — until you realize almost everything is recoverable through git reflog. Master the daily commands, agree on a workflow with your team, use a visual tool like GitKraken or VS Code’s built-in Git when conflicts get hairy, and never force-push to shared branches.
The single biggest jump in productivity comes from agreeing on a workflow as a team and writing it down. Whether it’s GitHub Flow, Git Flow, or trunk-based, the worst Git setup is one where every developer does something different.
When something goes catastrophically wrong: stop, breathe, run git reflog, and you’ll almost always find your work waiting for you.
Be the first to comment