We are now able to create different branches in the local repository as well as different branches on our remote repository (GitHub). In the real world when you create different branches and work on them, eventually you need to combine them with the master branch. This process is called Merging branches.
We will be discussing how to merge branches, the different types of merging and also some of the conflicts that might occur when merging the branches.
Here we are going to cover:
- Git Merge (fast forward)
- Git Merge (3-way-merge)
- Merge Conflict
- Git Rebase
Firstly, lets go to our local repo and see which branches we have. Then lets look at the history of our commits so we know where we are. Finally lets create and switch into a new branch, lets call it playground.
After creating and switching into our new branch lets modify the style.css file and save the changes. Now we can git add these modifications to the staging area and finally make our git commit.
There is an easier way to
git add and then
git commit our file. This chapter is all about merging so it's appropriate to merge these two commands we use so frequently.
We can use the command:
git commit -am
This command is followed by the commit message. Now we are adding our file to the staging area and making the commit simultaneously!
This is a shorter way, but just remember that this command only works for modified and deleted files. If you have new files which are not tracked, this command will not work!
We can make another commit for good measure. We can modify the style.css file again and this time change the color of the paragraph and save. Finally add and commit our changes.
As you can see the playground branch is now two commits ahead of the master branch. So now lets say we are happy with our changes and we want to merge those two branches. At first we need to switch to the master branch.
We will see our master branch does not have the changes we made on the playground branch and we can confirm this by checking the history of our commits. You will see that the two commits we made are not in the master branch commit history.
To merge these two branches we need to run the command:
This command is followed by the name of the branch you want to merge with the master. For example:
git merge playground
This command has now merged our two branches and if we look at our logs
git log --oneline. We will see that HEAD now points to both branches, master and playground.
We have successfully combined two different branches and this type of merge is really basic and it is called fast forward merge.
In the case of a fast forward merge, there is a linear path from the master branch to the second branch, which in this case is playground. The playground branch is created based on the last commit of the master branch. Meaning while we were working on the playground branch, no commits were being made to the master branch.
When we use the
git merge command, all the commits from the playground branch were added after the base commit, which is the last commit of the master branch .
Therefore any changes made to the playground branch will overwrite the old state of the master branch and there won't be any conflicts.
To prove this, lets switch back to the playground branch. Make a modification to the style.css file and save it. Then make a new commit. We can then go back to the master branch and run
git merge playground again.
That is the way we can merge two branches in a fast forward way. There is another type of merge which we are going to discuss next.
The other type of merge we can perform is called the three-way merge. This kind of merge occurs when we create a new branch, work on that branch...Then simultaneously someone else makes commits on the master branch before we merged the two.
Actually this is pretty common and knowing how to work with this type of problem is essential. So lets simulate this kind of scenario by making two changes in our script.js file in our playground branch, finally adding and commiting the changes.
Switch to the master branch, make a different change in the index.html file and then add and commit the change.
Don't forget to log the history of both branches once you've made your changes to see the differences.
Now suppose we want to merge these two branches. In this case we will no longer have a fast forward merge because we've been working on both branches simultaneously. So lets go ahead and merge these two branches.
Run the command
git merge playground
As you can see we are greeted with a new file in the editor, also Git is hinting to us that it is waiting for us to close the file in the editor. We don't need to do anything in this file for now, so we can quit out of it and close.
We can see our merge performed successfully, however on closer inspection, we can see it is a different type of merge which is called three-way merge. After the merge it says merge made by 'recursive' strategy which is the default strategy of a three-way merge.
Now all the changes we made on the playground branch is available on the master branch as well, unlike the fast forward approach HEAD points to master and not to both branches.
That is why we see only master next to the HEAD pointer and not both branches like we did before.
If we switch to the playground branch we will see the index.html file was not modified in our commit history and we will find only the commits we made on this branch.
When we create a new branch, make some changes/commits to that branch, then simultaneously someone else makes commits to the master branch. It means we no longer have fast forward merge, instead we now have a merge type called three-way merge.
In this case Git will help us by checking for any conflicts in the file because people may work on the same files. If all is good then those changes will be merged to master and the commits will be place in a sequence in history.
This is the case when a three-way merge occurs.
Seems like Git solves our issues for us but we can still run into some merge conflicts dispite Git's best efforts.
Sometimes multiple developers may try to edit the same content. For example, if one developer is trying to edit code that the second developer is editing. Then merge conflicts can occur.
Merging and conflicts are a common part of Git, in other version control systems it is slightly harder to deal with merge conflicts but Git make this easy for us. We can simulate how a merge conflict may occur and how we can deal with them.
Firstly, lets go to our project and see all our branches. Currently we have two branches, namely master and playground. We are working on the playground as indicated by the green color. Lets say we want to modify the variable in the script.js file in our playground branch. We then save our change and make a commit.
Now lets say someone else is editing the same script.js file on the same line but he/she is working from the master branch. We can simulate this scenario like we did before.
We will first switch to the master branch, change the name of the variable in the script.js file and save, we will commit this change to history and finally log the history.
Suppose we need to merge these two branches, when we run the command
git merge playground Git will tell us that there has been a merge conflict. This is because we have been working on the same line in different branches, so Git was not able to decide which line/version it needs to keep.
Therefore a conflict has arised.
As you can see both versions are present in the script.js file as well as some code that Git inserted.
To resolve this problem, we first need to remove the code Git has added and then we need to decide which version of the code we want to keep.
After that we need to add the file to the staging area. Like this:
git add .
Then we need to make a commit, but in this case we just need to run the command without any message:
Now a new file will open in the editor, like in the case of a three-way merge. Here we can change the message of the commit if we want.
When we close the file, the new commit will be made. The branches will be merged and the conflict will be resolved.
If we run the history we will see merge branch playground which means we have merged the branches successfully.
As we did before, we will be simulating two developers making a change on the same line of the same file, but just from different branches.
We will again use the script.js file and make some changes and commit them to history. Finally after editing the script.js file on both branches we will run the command
git merge playground.
Now suppose we want to stop merging. To do this we need to run the command:
git merge --abort
Git will stop the merge from happening, there won't be a merging conflict anymore and both changes we made on the two branches will be kept.
In the real world, these types of conflict appear quite frequently but thanks to Git they are easy to deal with.
Before carrying on we can resolve the merge conflict we just had.
We are now familiar with the Git Merging and how to resolve merge conflicts, it is now time for us to discuss another feature of Git which is called Git Rebase.
Suppose we have a couple of commits on the master branch, then we create a new branch and add some commits to it. Then after that we create a new commit on the master branch.
In this case, by default base commit is the commit from where we created a new branch. But by using Git Rebase we are able to change the base commit, essentially rebasing our branch on the newly created commit on the master branch.
After Rebasing, all the changes we made on the master branch will be available on the second branch as well.
In order to make things look a little cleaner, I am going to delete some of my commits by using
git reset --hard
Next I'm going to delete the playground branch and create a new branch called newBranch.
When we look at the history of our commits, we see that HEAD points to newBranch and master. This means that they have identical histories.
Lets switch back to the master branch and make a new commit. For example, we can modify the style.css file and change the size of the h1 tag. We save and then commit this change.
If we log the history again, we see that the master branch is now 1 commit ahead of the newBranch. This means that this new commit won't be available on the newBranch. We can switch to the newBranch to confirm this by running the command
git log --oneline.
If we want to make the last commit from the master branch available on the newBranch as well, we can use
git rebase which will allow us to rebase the newBranch on the newly created commit which was made on the master branch.
We do this by running the command:
git rebase master
Now when we log the history, we can see HEAD points to both branches, newBranch and master. It means that the last commit from the master branch is available on the newBranch as well.
Lets consider again how
git rebase works using a different scenario. We start by making a couple commits on the newBranch, then we log the history of our commits.
Lets switch back to the master and make one commit. When we save our change and make the commit, if we check the history we see the commits of the master branch but not those for the newBranch.
To address this we switch back to the newBranch and rebase it to the last commit made by the master branch. After we rebase the newBranch, when we check the history of commits, we see the last commit made by the master branch as well as the two commits we made on the newBranch.
Now all the commits are available on the newBranch, but if we go to the master branch and check the history, you will find that we still have the commits we made only on the master branch.
In order to make commits on the newBranch, available on the master branch as well, we can merge those branches by running the command
git merge newBranch
As you can see the branches are merged. You may notice that instead of a three-way merge, we instead have a fast-forward merge. This is because we rebased the newBranch on the last commit of the master branch, that is why Git used the fast-forward merge.
That's it! I hope you enjoyed and gained something from this chapter.