Both merge and rebase offer the same service which is incorporating commits from one branch into another, but how is this done?
Given the two following branches
A---B---C Release / D---E---F---G Development
And given that the current branch is Development, when you perform a merge:
git merge Release
The merge will replay all the changes that have been made on Release since it diverged from Development up to commit C, on top of Development, recording the result in a new commit along with the names of the two parent commits and a log message from the user describing the changes
A---B---C Release / \ D---E---F---G---H Development
An example of a merge commit from some code
commit ca617c46f71507d1bdf44d452e1a7f0a6740c344 Merge: ea47a12b3 e16cbd937 Author: Foo Bar <email@example.com> Date: Wed Mar 18 12:42:03 2020 +0000 Merge pull request #836 in Baz/funny-services from bar/FAB-579 to development * commit 'e16cbd937937bd368c28bd7677f211997231a4f1': FAB-479 removed breaking dependency
Whats is the downside of merge? If used too liberally, merge commits could clutter your git log, making difficult to understand the flow of the project changes:
git log --oneline --graph --decorate --all |/ / / * | | d61503b9e (tag: 1.0.14) Merge pull request #753 in FUN/funny-services from foobaz/TEAM-487-a to development |\ \ \ | * | | ea8052a6c (origin/foobaz/TEAM-487-a) TEAM-487 add mongo ssl/auth for progressive * | | | 534cc7abf Merge pull request #745 in FUN/funny-services from foobaz/TEAM-487-a to development |\ \ \ \ | |/ / / | * | | 2bc74b0c7 TEAM-487: Customer - some nice stuff |/ / / * | | 721565549 Merge pull request #752 in FUN/funny-services from foo/HEART-93-mongoclient-auth to development |\ \ \ | * | | c1ba829fd (origin/foo/HEART-93-mongoclient-auth) HEART-93 lib release versions. | * | | 9f3d3a443 Merge branch 'development' into foo/HEART-93-mongoclient-auth | |\ \ \ | |/ / / |/| | | * | | | 82e3b898d Merge pull request #751 in FUN/funny-services from baz/TEAM-A-638 to development |\ \ \ \ | * | | | f920814a5 (origin/baz/TEAM-A-638) TEAM-A-638 Fixed variable name | * | | | 688a166d8 TEAM-A-638 Added missing content * | | | | 271e8d3c8 Merge pull request #744 in FUN/funny-services from baz/TEAM-A-638 to development |\ \ \ \ \ | |/ / / / | * | | | 1ebb5100a TEAM-A-638 removed useless comments | * | | | d03467db6 TEAM-A-638 refactored packages and classes | * | | | 08064700b TEAM-A-638 renamed controller file name | * | | | 59c93e015 TEAM-A-638 add deactivation control and model + tests * | | | | 8c3b6b33a Merge pull request #747 in FUN/funny-services from foobaz/TEAM-528 to development |\ \ \ \ \ | * | | | | f61fa3a90 (origin/foobaz/TEAM-528) TEAM-528: upload tool to source control. | |/ / / / | | * | | 9a2cc0515 HEART-93 changes to support catch-up. | | * | | 636f5db20 Merge branch 'development' into foo/HEART-93-mongoclient-auth | | |\ \ \ | |_|/ / / |/| | | | * | | | | 946f4e8d4 Merge pull request #749 in FUN/funny-services from bar/FAB-420-resolve-client-ids to development |\ \ \ \ \ | * | | | | 97531eb5a (origin/bar/FAB-420-resolve-client-ids) FAB-420 Fixed missing error message | * | | | | d54f7c7da FAB-420 Added client id * | | | | | fbb71d8e1 Merge pull request #748 in FUN/funny-services from foobaz/TEAM-419-add-back-in to development |\ \ \ \ \ \ | |/ / / / / |/| | | | | | * | | | | caad3ef9a (origin/foobaz/TEAM-419-add-back-in) TEAM-419: Customer - re-adding endpoint for Customer |/ / / / / | | * | | 91588ab17 Merge branch 'development' into foo/HEART-93-mongoclient-auth | | |\ \ \ | |_|/ / / |/| | | | * | | | | 70aa26022 Merge pull request #733 in FUN/funny-services from foobaz/TEAM-503 to development
Given the same branches as before:
A---B---C Release / D---E---F---G Development
suppose that the current branch is Release and that you want to update it, to include all the latest changes that have been merged into Development. This the case where rebase is really helpful:
git rebase Development D---E---F---G---A'---B'---C' Release
To perform this operation Git goes back in the history until it finds the common ancestor ( commit E ), gets all the diffs introduced by your changes and saves them in temporary files. It then resets the current branch ( Release) to the same commit as the branch you're rebasing onto ( Development commit G) and then finally applies all your changes in turn. You then end up with a clean and linear history.
Keep in mind that, although the applied changes are exactly the same, from Git perspective these are new commits with a new SHA, meaning that you're rewriting the history of your branch.
From Git manual, the main difference between merge and rebase:
Rebasing replays changes from one line of work onto another in the order they were introduced, whereas merging takes the endpoints and merges them together.
When to use merge and when to use rebase
- Use merge if you want a set of commits clearly grouped in history
- User rebase if you want a linear history and if you're contributing to a bigger projects that you don't control. In this case rebasing your branch means that you solve all the conflicts locally and the project's maintainer does not have to do any integration work.
- Do not use rebase on a shared branch - Don't rebase Development/Main onto a working branch.