Handling Long-Lived Feature Branches and Rebasing
When using Gitflow, long-lived feature branches can create complications if other branches are based on them and the original feature branch is later merged to master via a squash merge.
This guide explains how to manage these situations, including using git rebase instead of git merge to avoid conflicts and how to effectively rebase branches after a squash merge.
1. Handling Extensive Commit Histories and Merge Conflicts
When the commit history is extensive, and there are multiple merge conflicts to address, it is advisable to do the rebasing work on a separate branch to ensure the changes are safe and verified. You can follow these steps:
-
Create a New Branch for Rebasing
git checkout -b feature/your-branch-name-rebase-1 -
Perform the Rebase
git rebase origin/target-branch -
Verify the Contents After rebasing, use
git diffto verify the differences between the original branch and the newly rebased branch:git diff feature/your-branch-name feature/your-branch-name-rebase-1 <filename>This helps you confirm that the intended changes are correct and no unintended changes were introduced.
2. Keep Feature Branches Up-to-Date Using git merge or git rebase
1. Scenario: Branch originated from master
In this scenario, the feature can be kept up-to-date either by merge or rebase.
Since the branch originated from master, both methods will bring the changes to the feature branch.
And in the PR view of this branch, we would see only related commits that are going to be merged to the master branch.
2. Scenario: Branch originated from another branch than master
Suppose we have two branches:
feature/feature-1: A branch that introduces major changes and is actively developed.feature/feature-2: A branch based onfeature/feature-1.
If feature/feature-1 is merged to master using a squash merge, feature/feature-2 will no longer have a straightforward history compared to master. This can create significant rebase complications.
Caveats:
- Rewriting History: Rebasing rewrites commit history, which may require force-pushing (
git push -f). Be careful not to overwrite others' work, and always communicate with your team if you are sharing branches.
3. Rebasing after originated branch is squash merged to master
When a feature branch, such as feature/feature-1, is squash merged into master, any branches based on it (e.g., feature/feature-2) need to be rebased to reflect the new state of master.
Considering that the PR branch has been deleted after merge, so the following process will help you rebase successfully.
Fetch deleted PR references
First, run the following command to ensure you have access to all remote references, including deleted PR branches:
git fetch origin +refs/*:refs/remotes/origin/*
- What this command does: This command fetches all references, even those that were automatically deleted after the PR was merged.
Locate the original branch for Rebasing
Next, locate the deleted branch that was merged via squash using:
git branch -r | grep "pull/PR_NUMBER/head"
- Note: Replace
PR_NUMBERwith the actual PR number. This will give you access to the latest version of the deleted branch.
Rebase feature branch onto master
Once you have identified the correct branch reference, rebase your feature branch (e.g., feature/feature-2) onto master:
# On feature/feature-2 branch
git rebase --onto master origin/pull/PR_NUMBER/head
- What this does:
git rebase --onto master: Rebases the branch starting from the point where it branched fromfeature/feature-1ontomaster.- Instead of giving directly
master, it is also possible to give the commit hash of the commit where thefeature/feature-1was merged intomaster. This might be needed if the changes in feature-2 is not yet compatible with the ones later inmaster. origin/pull/PR_NUMBER/head: Uses the latest commit from the originalfeature/feature-1branch before it was squashed and merged.
Verifying Changes:
After the rebase, verify the new commit history with:
git log --oneline -30 | cat
Ensure that the changes align with the intended commits after the rebase process.
Resolving Conflicts
During the rebase, you may need to resolve conflicts. Always pay careful attention to conflicts that arise and resolve them thoughtfully to maintain consistency with the changes from master.
Branch Tree for Squash Merge Scenario
Before Squash Merge of feature/feature-1 into master
A---B---C master
\
D---E---F feature/feature-1
\
G---H feature/feature-2
Current status:
master...feature-1 -> 3 commits ahead, 1 commit behind
master...feature-2 -> 4 commits ahead, 1 commit behind
feature-1...feature-2 -> 2 commits ahead, 1 commit behind
After Squash Merge of feature/feature-1 into master
In repository, you can still access to reference of the branch even if ti was deleted.
A---B---C---S master
\
D---E---F origin/pull/PR_NUMBER/head (deleted feature/feature-1)
\
G---H feature/feature-2
=
(S is the squash merge commit representing all changes from feature/feature-1. origin/pull/PR_NUMBER/head retains commits D, E, F as they originally were, and feature/feature-2 is still based on these commits.) If the feature-2 branch was kept rebased on top of the feature/feature-1 branch, it would have the F commit as the base, and it would be easier to rebase it onto the master branch.
Current status:
master...feature-2 -> 4 commits ahead, 2 commit behind
After Rebasing feature/feature-2 onto master with git rebase --onto master origin/pull/PR_NUMBER/head
A---B---C---S master
| \
| G'---H' feature/feature-2
\
D---E---F origin/pull/PR_NUMBER/head (deleted feature/feature-1)
(G' and H' are the rebased versions of commits G and H.)
Current status:
master...feature-2 -> 2 commits ahead