Understanding Git Merge vs Rebase

A comprehensive guide to branch integration strategies in Git

Featured image



Overview

Git’s Merge and Rebase commands both integrate changes between branches but differ significantly in how they manage project history.

Merge combines the work of multiple branches into a single merge commit, preserving a non-linear history that clearly shows the workflow of different branches.

Rebase replays the commits from one branch onto another branch’s latest commit, maintaining a linear history that can be easier to follow.

Additionally, understanding the concepts of “ours” and “theirs” during conflict resolution is crucial, as their meanings change depending on whether you’re performing a merge or rebase operation.

This guide provides practical examples of these concepts, interactive rebase commands, and strategies for choosing the appropriate method for different situations. It will help you maintain a clean history during collaboration and effectively resolve branch conflicts.


Git Rebase

Rebase is a powerful Git feature used to integrate changes from one branch to another by moving or “replaying” commits from a feature branch onto another branch (typically the main branch). The primary goal of rebase is to create a linear project history.

gitGraph commit id: "A" commit id: "B" branch feature checkout feature commit id: "C" commit id: "D" checkout main commit id: "E" commit id: "F" checkout feature

Before Rebase: Feature branch has commits C and D while main has progressed with E and F

gitGraph commit id: "A" commit id: "B" commit id: "E" commit id: "F" branch feature checkout feature commit id: "C'" commit id: "D'"

After Rebase: Feature branch commits are replayed on top of main, creating a linear history


Functions in Rebase

Several common functions are available during an interactive rebase (git rebase -i):


Git Merge

Merge is used to integrate changes from one branch into another by creating a new “merge commit” that combines the changes from the merged branch. This creates a non-linear history.

gitGraph commit id: "A" commit id: "B" branch feature checkout feature commit id: "C" commit id: "D" checkout main commit id: "E" commit id: "F"

Before Merge: Feature branch diverges from main with its own commits

gitGraph commit id: "A" commit id: "B" branch feature checkout feature commit id: "C" commit id: "D" checkout main commit id: "E" commit id: "F" merge feature id: "Merge commit"

After Merge: A merge commit combines changes from both branches, preserving the non-linear history


Understanding “ours” and “theirs” in Git

When resolving conflicts in Git, especially during merge and rebase operations, “ours” and “theirs” provide ways to reference changes from different points.

Operation ours theirs Description
Merge Changes in the current branch Changes in the branch being merged During a merge, "ours" refers to the current branch (target branch for the merge), and "theirs" refers to the branch being merged. Used to determine which changes to prioritize when conflicts occur.
Rebase Changes in the branch being rebased Changes in the target branch for rebase During a rebase, the meaning of "ours" and "theirs" is reversed compared to merge. "ours" refers to the original branch being rebased, and "theirs" refers to the branch that becomes the new base. This reversal can cause confusion and requires careful attention.


In Merge

%%{init: { 'gitGraph': {'showBranches': true, 'showCommitLabel': true}} }%% gitGraph commit id: "Base" branch feature checkout feature commit id: "Feature change" checkout main commit id: "Main change"
flowchart LR C[Conflict on same file] C --> M["git merge feature
(from main branch)"] M --> O["--ours
keeps 'Main change'"] M --> T["--theirs
keeps 'Feature change'"] style O fill:#d4f4dd,stroke:#333,stroke-width:1px style T fill:#ffd3b6,stroke:#333,stroke-width:1px style M fill:#b3e5fc,stroke:#333,stroke-width:1px

During a merge, "ours" refers to the current branch (main) and "theirs" refers to the branch being merged (feature)


In Rebase

%%{init: { 'gitGraph': {'showBranches': true, 'showCommitLabel': true}} }%% gitGraph commit id: "Base" branch feature checkout feature commit id: "Feature change" checkout main commit id: "Main change"
flowchart LR C[Conflict on same file] C --> R["git rebase main
(from feature branch)"] R --> O["--ours
keeps 'Feature change'"] R --> T["--theirs
keeps 'Main change'"] style O fill:#ffd3b6,stroke:#333,stroke-width:1px style T fill:#d4f4dd,stroke:#333,stroke-width:1px style R fill:#b3e5fc,stroke:#333,stroke-width:1px

During a rebase, "ours" refers to the branch being rebased (feature) and "theirs" refers to the target branch (main) - opposite meanings compared to merge


Practical Usage

Merge Scenario

When merging two branches and conflicts occur, you can choose between “ours” or “theirs” resolution methods depending on your perspective (target or incoming branch) and which changes you want to prioritize.

# Accept our version during merge conflict
git merge -X ours feature-branch

# Accept their version during merge conflict
git merge -X theirs feature-branch

# During conflict resolution, choose specific files
git checkout --ours path/to/file.txt
git checkout --theirs path/to/file.txt

Rebase Scenario

Choosing between “ours” and “theirs” during a rebase can be more confusing because the roles switch depending on the changes and base context. Remember that “ours” is the branch being rebased (current) and “theirs” is the branch being rebased onto (conflicting).

# During rebase conflict resolution
git checkout --ours path/to/file.txt   # Keep the feature branch version
git checkout --theirs path/to/file.txt # Keep the main branch version

# After resolving conflicts
git add path/to/file.txt
git rebase --continue


Merge is often used with options like --no-ff (no fast-forward) to create a merge commit even in cases where a fast-forward merge would be possible, preserving information about the past existence of a feature branch.

# Create a merge commit even if fast-forward is possible
git merge --no-ff feature-branch

# Merge with a custom commit message
git merge --no-ff -m "Merge feature X into main" feature-branch

Rebase is frequently used with --interactive to clean up a series of changes before merging them into the main project branch.

# Interactive rebase for the last 3 commits
git rebase -i HEAD~3

# Rebase feature branch onto main
git checkout feature-branch
git rebase main

# Interactive rebase onto main
git rebase -i main


Conclusion

Git’s Rebase and Merge are vital branch integration tools, each with its own purpose and advantages.

Merge safely preserves changes between branches and maintains a record, while Rebase is effective for maintaining a clean, linear commit history.

However, these two methods can cause confusion because “ours” and “theirs” are interpreted differently during conflict resolution. It’s crucial to choose and use these commands carefully based on a thorough understanding.

In team collaboration, discussing how to manage commit history and which strategy is better is essential. Flexibility in choosing between Rebase and Merge based on the situation is necessary.

Ultimately, consistency in history and efficiency in collaboration are what matter. Keeping things well-organized helps greatly with tracking history and debugging later.



References