← All posts

Git Merge, Rebase & Squash — Part 6: Team Workflows and the Decision Guide

Data-Sci & Digital HealthBasic InfoTech & Computing Nexus
Git Merge, Rebase & Squash — Part 6: Team Workflows and the Decision Guide
On this page

Abstract

Establishing systematic version control workflows is critical for development teams aiming to maintain a coherent, recoverable, and readable repository history. While individual developers must master repository mechanics, shared guidelines prevent destructive collisions and preserve integration context. Platform pull requests strictly map to foundational operations: a standard merge preserves the complete commit graph, a squash condenses a branch into a single commit, and a rebase rebuilds commits upon a new base to synthesize a linear timeline with altered hashes. To safely arbitrate timelines, practitioners must differentiate a reset, which retroactively shifts branch pointers, from a revert, which generates a new commit to negate past changes on shared branches without losing data. Furthermore, teams enforce history policies by configuring explicit behaviors like fast-forward merges, utilizing cherry-pick for surgical code backports, and applying the reflog to rescue seemingly lost work. This article translates these distinct operational mechanics into comprehensive decision frameworks for standardizing repository management.


Infographic visual summary of Git Merge, Rebase & Squash — Part 6: Team Workflows and the Decision Guide
Visual summary · ภาพสรุป

This is the finale of the series. The previous five parts explained the mechanics: the commit graph and every kind of merge, squash merge, rebase, interactive rebase, and how to handle conflicts and merge strategies. Part 6 turns all of that into decisions — which operation to reach for in each situation, how GitHub's Pull Request buttons map onto the concepts you already know, and how a team can agree on a workflow that keeps its history clean and recoverable.

After this part you will be able to pick the right tool for any everyday Git situation from a single decision table, run a safe feature-branch workflow from first commit to merged PR, tell reset apart from revert and rebase, revert a merge commit correctly, and rescue work you thought you had lost with the reflog.

GitHub's three Pull Request merge methods

When you merge a Pull Request on GitHub you are usually offered three buttons. Each maps directly onto a concept from earlier parts.

Create a merge commit

Keeps every commit from the branch and adds a merge commit on top. The full development history of the PR stays visible on the target branch.

Squash and merge

Combines the entire Pull Request into a single commit on the target branch — one PR becomes one commit.

Rebase and merge

Recreates the PR's commits on top of the target branch with no merge commit. Because the commits are rebuilt on a new base, their commit hashes on the target branch will change — GitHub always recreates the commits, even when a fast-forward would have been possible.

"Rebase and merge" gives you the individual commits and a linear history — but remember the hashes are new, exactly as with a local rebase.

How cherry-pick fits in

Cherry-pick is not a merge, but it belongs in the same mental toolbox.

git cherry-pick <commit-hash>

It means:

Copy the changes from one commit and create them as a new commit on the current branch.

Before, starting from:

A---B---C  main
     \
      D---E  feature

On main, run:

git cherry-pick E

After:

A B C E′ D E copy main feature
git cherry-pick E copies commit E onto main as the new commit E′; the original E stays on feature.

Notice the new commit is E', a fresh copy — not the original E. Use cherry-pick when you want to:

Do not use cherry-pick to pull in an entire feature branch in place of a real merge without a good reason — doing so produces duplicated history.

Reset is not the same as Rebase

Reset

Reset moves the branch pointer.

git reset --soft HEAD~1
git reset --mixed HEAD~1
git reset --hard HEAD~1

Rebase

Rebase replays commits onto a new base.

git rebase main

Both can rewrite history, but their purposes differ: reset repositions where a branch points, while rebase rebuilds the commits themselves on top of a different starting point.

Revert is not the same as Reset

Revert

Revert creates a new commit that undoes the effect of an older commit.

git revert <commit-hash>

It suits shared branches because it does not delete history.

Reset

Reset moves the pointer backwards and can make commits disappear from the branch.

git reset --hard HEAD~1

It suits local history that has not been shared yet.

Reverting a merge commit

A merge commit has more than one parent, so you must tell Git which parent is the mainline.

git revert -m 1 <merge-commit-hash>

Think of a three-way merge, where M has two parents:

D E A B C F G M feature/login main 1 2 parent 1 = G (mainline) parent 2 = E (feature)
The merge commit M has two parents — parent 1 = G on main (the mainline) and parent 2 = E on feature/login; git revert -m 1 keeps the mainline and undoes the feature side.

In general, -m 1 means "treat the first parent as the main branch, then undo the changes that came in from the branch that was merged." Always inspect the parents first:

git show <merge-commit-hash>

What git pull actually does

By default:

git pull

is conceptually equivalent to:

git fetch
git merge

which may create a merge commit.

Pull with rebase

git pull --rebase

is equivalent to:

git fetch
git rebase origin/main

Pull that never creates a merge commit

git pull --ff-only

It is better to configure explicit behaviour than to leave it to chance.

Make pull always rebase

git config --global pull.rebase true

Make pull fast-forward only

git config --global pull.ff only

Pick one according to your team's workflow.

Step 1 — update main

git switch main
git pull --ff-only

Step 2 — create a feature branch

git switch -c feature/login

Step 3 — work and commit

git add .
git commit -m "feat: add login form"

git add .
git commit -m "test: add login validation tests"

Step 4 — update the base before opening a PR

git fetch origin
git rebase origin/main

Resolve conflicts if any arise:

git add .
git rebase --continue

Step 5 — push

If you have not pushed before:

git push -u origin feature/login

If you had already pushed before the rebase:

git push --force-with-lease

Step 6 — merge the PR

Choose according to your team policy: Squash and merge, Rebase and merge, or a merge commit.

How to choose for each situation

Use a normal merge when

git merge --no-ff feature/login

Use rebase when

git switch feature/login
git rebase main

Use squash merge when

git switch main
git merge --squash feature/login
git commit -m "feat: add login system"

Use fast-forward only when

git merge --ff-only feature/login

Use cherry-pick when

git cherry-pick abc1234

Decision table

The whole series distilled into a single lookup: find your situation on the left, use the approach on the right.

Situation Recommended approach
Private branch needs to catch up with main git rebase main
Shared branch worked on by several people Merge
PR has messy commits Squash merge
Keep every commit but keep history linear Rebase and merge
You want the feature boundary to be visible merge --no-ff
You need only a single bug-fix commit Cherry-pick
Production branch must undo a change Revert
The latest local commit is wrong Amend or interactive rebase
Merge commits are not allowed --ff-only

A policy for small teams

For a small team, this shape works well:

main
  └── protected, no force push

feature/*
  └── the author may rebase

Pull Request
  └── use Squash and merge

The workflow:

git switch feature/my-feature
git fetch origin
git rebase origin/main
git push --force-with-lease

Then use Squash and merge. The result on main is one clean commit per PR:

A B C Feature1 Feature2 BugFix main
Squash-and-merge policy: main stays a straight line with one commit per feature — Feature1, Feature2, BugFix.

One PR equals one commit. This suits SaaS products, web applications, and any team that wants an easy-to-read history.

A policy for projects that need detailed history

When you must keep the full development record, use a branch model like:

main
develop
feature/*
release/*
hotfix/*

and integrate with:

git merge --no-ff feature/x

The result:

This suits large systems, multiple release lines, work that must be auditable, and cases where you need to trace exactly which steps a feature went through.

Reading the Git graph

To see the graph clearly:

git log --graph --oneline --decorate --all

Example output:

*   a8f9210 (HEAD -> main) Merge feature/login
|\
| * 73ab110 Add validation
| * 19c4d20 Add login form
* | 10fc991 Update dependencies
|/
* 61ad820 Initial commit

A handy alias:

git config --global alias.graph "log --graph --oneline --decorate --all"

Then simply run:

git graph

Recovering from mistakes

Git can usually still rescue you through the reflog.

git reflog

Example:

a123456 HEAD@{0}: rebase finished
b234567 HEAD@{1}: rebase started
c345678 HEAD@{2}: commit: working version

Recover the branch:

git branch recovery c345678

or:

git reset --hard c345678

Before using reset --hard, make sure you do not need any uncommitted changes.

One sentence per command

The most important rules to remember

Merge preserves history. Rebase rewrites history. Squash compresses history.

And:

You can rebase local/private history, but avoid rebasing shared, public history.

For a workflow that is both safe and clean:

git switch feature/my-feature
git fetch origin
git rebase origin/main
git push --force-with-lease

Then use Squash and merge once the PR has passed review.

Key takeaways

0
Message for International and Thai ReadersUnderstanding My Medical Context in ThailandRead more →Message for International and Thai ReadersUnderstanding My Broader Content Beyond MedicineRead more →

Comments

No comments yet. Be the first to share your thoughts.

Sign in to comment