Updating one branch with another – git rebase and git merge
Both commands – “git rebase” and “git merge” update a branch with commits from another branch. Below are the most typical uses based on best practices.
git rebase
“git rebase” should be typically used when you want to update a target branch with all the commits from a source branch. All the commits are applied one after another. The goal is to keep the target branch updated with the latest changes in the source branch. The target branch may have some more extra commits of its own.
Let us see below with a real time example and hands-on.
1. Initial Setup
In a production environment, you would never directly do “git push” onto a git repository. You fork the online git repository of production-ready code and have your copy. After your work, you raise a pull request for a code review. After code review, your code in the local branch is merged into the online repository’s branch.
Let us say, the online git repository is origin and the branch is main.
~/repoone$ git remote -vv
origin git@github.com:codeversionmaster/repoone.git (fetch)
origin git@github.com:codeversionmaster/repoone.git (push)
~/repoone$ git branch -a
main
remotes/origin/HEAD -> origin/main
remotes/origin/main
You would create a copy of the online repository using a fork. Let us call this a local repository. It would have a branch called main. You will usually keep this untouched by your work and keep it regularly updated to origin/main.
~/repoone$ git branch
* main
You would still create a copy of this, call it as maincopy, and do your work on this branch.
$ git branch
main
* maincopy
All three branches have two commits as of now, as shown.
$ git log --oneline remotes/origin/main
453424e (origin/main, origin/HEAD, main) This is first commit
2673111 Initial commit
~/repoone$ git log main --oneline
453424e (HEAD -> maincopy, origin/main, origin/HEAD, main) This is first commit
2673111 Initial commit
~/repoone$ git log main --oneline
453424e (HEAD -> maincopy, origin/main, origin/HEAD, main) This is first commit
2673111 Initial commit
Now, while you are busy working in your local repository, other developers might contribute in origin/main. Two new commits got added in origin/main, as shown.
$ git log --oneline remotes/origin/main
908d6ac (origin/main, origin/HEAD, main) Third commit in main
42296d0 Second commit in main
453424e (HEAD -> maincopy) This is first commit
2673111 Initial commit
2. “git rebase” can bring local main branch in sync with origin
Use “git rebase” to get your main branch in sync with origin/main.
$ git branch
* main
maincopy
$ git pull --rebase origin main
Warning: Permanently added the ECDSA host key for IP address '----' to the list of known hosts.
From github.com:codeversionmaster/repoone
* branch main -> FETCH_HEAD
Updating 453424e..908d6ac
Fast-forward
filefive | 0
filefour | 0
filesix | 0
filethree | 0
4 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 filefive
create mode 100644 filefour
create mode 100644 filesix
create mode 100644 filethree
Current branch main is up to date.
All commits from source origin/main get applied to your local main. The commits are applied one after another.
$ git log main --oneline
908d6ac (HEAD -> maincopy, origin/main, origin/HEAD, main) Third commit in main
42296d0 Second commit in main
453424e This is first commit
2673111 Initial commit
3. “git rebase” can update your working copy with the main branch commits
Let us say you are working on your local branch maincopy. Before your work, it had two commits like seen before. Check as below.
~/repoone$ git log maincopy --oneline
453424e (HEAD -> maincopy) This is first commit
2673111 Initial commit
You did some great work and added two commits in this local branch maincopy.
$ git log --oneline
bae4665 (HEAD -> maincopy) Second local commit of maincopy
bfcb2d0 First local commit of maincopy
453424e This is first commit
2673111 Initial commit
As you have seen earlier, main branch got two new commits while you were busy adding new commits to your local maincopy branch. Those commits are from other developers. You want to have your branch maincopy updated with them and continue your work. Then, you use git rebase again. Do “git checkout” to maincopy and check the state of each branch as shown.
~/repoone$ git checkout maincopy
Switched to branch 'maincopy'
~/repoone$ git log main --oneline
908d6ac (origin/main, origin/HEAD, main) Third commit in main
42296d0 Second commit in main
453424e This is first commit
2673111 Initial commit
~/repoone$ git log maincopy --oneline
bae4665 (HEAD -> maincopy) Second local commit of maincopy
bfcb2d0 First local commit of maincopy
453424e This is first commit
2673111 Initial commit
Do git rebase to get all commits from main to maincopy. The local commits will remain in maincopy. If any conflicts are there, it will show so that you can change your code to resolve the conflicts.
$ git rebase main
First, rewinding head to replay your work on top of it...
Applying: First local commit of maincopy
Applying: Second local commit of maincopy
Note: You could have also done “git rebase origin main” in case you did not have local copy of main.
Now you can see that maincopy not only has local commits but applied all commits from main one after another.
$ git log --oneline
59968f9 (HEAD -> maincopy) Second local commit of maincopy
10d3907 First local commit of maincopy
908d6ac (origin/main, origin/HEAD, main) Third commit in main
42296d0 Second commit in main
453424e This is first commit
2673111 Initial commit
Above is a typical usage scenario. You can use “git rebase” whenever you want to guarantee that all the commits (code changes) of one branch are present in another.
git merge
As seen above, you would work in your local copy and keep adding commits in your local branch. When you raise a pull request, what you are requesting is this. You have extra code in the form of commits in your branch maincopy that you want to get into the online repository’s branch.
You want these changes to go into the online repository. The history of commits before your new local commits is common in source and target branches. Source main can have other commits after maincopy diverged from main. But history before that should be common. Your new commits will go on top of existing commits in main branch. This is when you use “git merge”.
Let us see this in a real-life example and hands-on.
The initial setup is as shown. You have a branch main and a branch maincopy.
~/repoone$ git branch
main
* maincopy
main and maincopy have the same four commits initially.
~/repoone$ git log main --oneline
908d6ac (origin/main, origin/HEAD, main) Third commit in main
42296d0 Second commit in main
453424e This is first commit
2673111 Initial commit
~/repoone$ git log maincopy --oneline
bae4665 (HEAD -> maincopy) Second local commit of maincopy
bfcb2d0 First local commit of maincopy
453424e This is first commit
2673111 Initial commit
maincopy diverged from main after the first four commits. And did its own two commits.
~/repoone$ git log maincopy --oneline
59968f9 (HEAD -> maincopy) Second local commit of maincopy
10d3907 First local commit of maincopy
908d6ac (origin/main, origin/HEAD, main) Third commit in main
42296d0 Second commit in main
453424e This is first commit
2673111 Initial commit
main had one commit extra while maincopy was doing two commits in parallel.
~/repoone$ git log --oneline
b831147 (HEAD -> main) Commit after maincopy created and having it's own commits
908d6ac (origin/main, origin/HEAD) Third commit in main
42296d0 Second commit in main
453424e This is first commit
2673111 Initial commit
Checkout to main, use git merge to merge commits from maincopy to main.
~/repoone$ git checkout main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
~/repoone$ git merge maincopy
Merge made by the 'recursive' strategy.
localfileone | 0
localfiletwo | 0
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 localfileone
create mode 100644 localfiletwo
This would open a window to get a merge message. Leave the default message as it is.
The main branch will have the new commits added.
~/repoone$ git log --oneline
f74196a (HEAD -> main) Merge branch 'maincopy' into main
b831147 Commit after maincopy created and having it's own commits
59968f9 (maincopy) Second local commit of maincopy
10d3907 First local commit of maincopy
908d6ac (origin/main, origin/HEAD) Third commit in main
42296d0 Second commit in main
453424e This is first commit
2673111 Initial commit
Note it has combined the two commits into a single commit “f74196a” and applied on top of the existing commits. “git merge” maintains the history.
What was the difference between rebase and merge above?
Let us try the same above merging using rebase instead of merge.
The initial setup is as shown. The first four commits in main and maincopy are the same. maincopy diverged and has two of its commits. main had its own one extra commit.
~/repoone$ git log main --oneline
87426b5 (HEAD -> main) Commit after maincopy created and having it's own commits
908d6ac (origin/main, origin/HEAD) Third commit in main
42296d0 Second commit in main
453424e This is first commit
2673111 Initial commit
~/repoone$ git log maincopy --oneline
59968f9 (maincopy) Second local commit of maincopy
10d3907 First local commit of maincopy
908d6ac (origin/main, origin/HEAD) Third commit in main
42296d0 Second commit in main
453424e This is first commit
2673111 Initial commit
Let us do rebase instead of merge.
~/repoone$ git rebase maincopy
First, rewinding head to replay your work on top of it...
Applying: Commit after maincopy created and having it's own commits
~/repoone$ git log --oneline
a5658c7 (HEAD -> main) Commit after maincopy created and having it's own commits
59968f9 (maincopy) Second local commit of maincopy
10d3907 First local commit of maincopy
908d6ac (origin/main, origin/HEAD) Third commit in main
42296d0 Second commit in main
453424e This is first commit
2673111 Initial commit
Listed below are differences between git rebase and git merge based on the outputs above.
git rebase | git merge |
~/repoone$ git log –oneline a5658c7 (HEAD -> main) Commit after maincopy created and having it’s own commits 59968f9 (maincopy) Second local commit of maincopy 10d3907 First local commit of maincopy 908d6ac (origin/main, origin/HEAD) Third commit in main 42296d0 Second commit in main 453424e This is first commit 2673111 Initial commit | ~/repoone$ git log –oneline f74196a (HEAD -> main) Merge branch ‘maincopy’ into main b831147 Commit after maincopy created and having it’s own commits 59968f9 (maincopy) Second local commit of maincopy 10d3907 First local commit of maincopy 908d6ac (origin/main, origin/HEAD) Third commit in main 42296d0 Second commit in main 453424e This is first commit 2673111 Initial commit |
“git rebase” rewinds HEAD and would apply the commits one after another. The commits may appear anywhere in the git log. | “git merge” creates a new hash commit here (f74196a) and applies that. |
Commits will appear merged into the original list of commits. | “get merge” would preserve the history of the merge. because of the new commit. |
That’s it. You should use “git rebase” when you want to update your code from a source branch and do your work on top of it. You don’t need others to know at which point code for merged. You should issue a merge request when you want your code to merge into the main source branch. Other developers should be able to see the merge commit, source, and target branch details.