QA Engineer Skills 2026QA-2026Rebase vs Merge

Rebase vs Merge

Two Ways to Integrate Changes

Both merge and rebase integrate changes from one branch into another, but they produce different commit history. Understanding the difference helps you keep a clean history, avoid conflicts, and work effectively with your team.


Merge

A merge creates a new "merge commit" that combines the histories of two branches. The full branch history is preserved in the graph.

git checkout main
git merge feature/login-tests
# Creates a merge commit; feature branch history visible in graph

What the history looks like after merge:

main:    A --- B --- C --- M (merge commit)
                    /     /
feature: D --- E --/

Both the main branch commits (A, B, C) and the feature branch commits (D, E) are visible. The merge commit (M) marks where integration happened.

Advantages of Merge

  • Preserves history: You can see exactly when a feature branch was created and merged
  • Safe for shared branches: Never rewrites existing commits
  • Easy to revert: Revert the merge commit to undo an entire feature

Disadvantages of Merge

  • Cluttered history: Many merge commits can make the log hard to read
  • Noisy diffs: Merge commits can make git log --oneline harder to scan

Rebase

A rebase replays your commits on top of the target branch, creating a linear history as if you had written your code after the latest changes on the target.

git checkout feature/login-tests
git rebase main
# Your commits are rewritten on top of main's HEAD

What the history looks like after rebase:

Before rebase:
main:    A --- B --- C
                \
feature:         D --- E

After rebase:
main:    A --- B --- C
                      \
feature:               D' --- E'  (new commits with same changes)

The commits D and E are replaced by D' and E' -- same changes, but new commit hashes. The history is linear.

Advantages of Rebase

  • Clean, linear history: No merge commits cluttering the log
  • Easier to review: PR diffs show only the feature changes, not merge noise
  • Simpler bisect: git bisect works more reliably on linear history

Disadvantages of Rebase

  • Rewrites history: Commits get new hashes, which is dangerous for shared branches
  • Can be confusing: If you rebase incorrectly, you may lose work
  • Conflict resolution is per-commit: You may need to resolve the same conflict multiple times if several commits touch the same file

When to Use Each

Situation Use Reason
Updating your feature branch with latest main Rebase Keeps your PR clean and easy to review
Merging a PR into main Merge (or squash merge) The merge commit marks the integration point
Integrating a long-lived branch Merge Preserves the complete history of the effort
Shared branch with multiple contributors Merge Never rebase commits others have based work on
Cleaning up commits before opening a PR Interactive rebase Combine WIP commits into logical, reviewable units

The golden rule: Never rebase commits that have been pushed to a shared branch and that others may have based work on. Rewriting shared history causes conflicts for everyone who has pulled those commits.


Resolving Conflicts During Rebase

When rebasing, Git replays each commit one by one. If a commit conflicts with the target branch, Git pauses and asks you to resolve it.

git rebase main
# CONFLICT (content): Merge conflict in tests/login.spec.ts
# Fix stopped at abc123f... Add login retry test

# Step 1: Open the conflicting file and resolve the conflict markers
# <<<<<<< HEAD
# current main version
# =======
# your version from the feature branch
# >>>>>>> Add login retry test

# Step 2: After resolving, stage the file
git add tests/login.spec.ts

# Step 3: Continue the rebase
git rebase --continue

# If you want to abort and go back to the state before rebase:
git rebase --abort

Tips for conflict resolution:

  • Read both versions carefully. Do not blindly accept one side.
  • If the conflict is in test code, check whether both sides added tests for the same feature. You may need to keep both.
  • Use git rebase --abort if you are unsure. You can always try again.
  • After resolving, run the tests locally to make sure the resolution is correct.

Interactive Rebase: Cleaning Up Before a PR

Interactive rebase lets you rewrite, combine, reorder, or drop commits before sharing them.

# Rebase the last 4 commits interactively
git rebase -i HEAD~4

This opens an editor with your commits:

pick abc1234 WIP: start login tests
pick def5678 fix typo
pick ghi9012 WIP: more login tests
pick jkl3456 finish login tests

Change the actions:

pick abc1234 WIP: start login tests
fixup def5678 fix typo                    # Merge into previous, discard message
squash ghi9012 WIP: more login tests      # Merge into previous, combine messages
pick jkl3456 finish login tests

Common actions:

  • pick: Keep the commit as-is
  • squash: Combine with the previous commit, edit the combined message
  • fixup: Combine with the previous commit, discard this commit's message
  • reword: Change the commit message
  • drop: Remove the commit entirely

When QA engineers should use interactive rebase:

  • Combine multiple "WIP" or "fix typo" commits into a single logical commit
  • Reword vague commit messages like "fix tests" to something descriptive
  • Remove commits that were debugging experiments (e.g., "add console.log")
  • Reorder commits so related changes are grouped together

Squash Merge

Many teams use squash merge when completing PRs. A squash merge combines all PR commits into a single commit on main.

# GitHub PR merge options:
# 1. Create a merge commit (preserves all commits)
# 2. Squash and merge (combines into one commit)
# 3. Rebase and merge (linear history, preserves individual commits)

QA preference: Squash merge is usually best for feature branches. It keeps main history clean (one commit per feature), and the detailed commit history is preserved in the PR itself.


Practical Workflow for QA Engineers

Here is the daily workflow that keeps your branches clean:

# 1. Start a new feature branch
git checkout main
git pull origin main
git checkout -b feature/add-checkout-tests

# 2. Work on your tests, making multiple commits
git add tests/checkout.spec.ts
git commit -m "WIP: add checkout happy path test"
# ... more work ...
git commit -m "add checkout error handling tests"
git commit -m "fix selector for payment button"

# 3. Before opening a PR, update with latest main
git fetch origin
git rebase origin/main

# 4. Clean up commits with interactive rebase
git rebase -i HEAD~3
# Squash "fix selector" into the relevant commit

# 5. Force-push to your feature branch (safe because it's your branch)
git push --force-with-lease origin feature/add-checkout-tests

# 6. Open PR. After approval, squash merge into main.

The --force-with-lease flag is safer than --force. It refuses to push if someone else has pushed to the branch since your last fetch, preventing you from accidentally overwriting their work.


Hands-On Exercise

  1. Create a feature branch, make 5 commits (including some "WIP" and "fix typo" commits)
  2. Rebase the branch onto the latest main
  3. Use interactive rebase to squash the WIP commits into clean, logical commits
  4. Intentionally create a conflict and practice resolving it during rebase
  5. Push with --force-with-lease and verify the clean history
  6. Open a PR and use squash merge to complete it