© Mariot Tsitoara  2020
M. TsitoaraBeginning Git and GitHubhttps://doi.org/10.1007/978-1-4842-5313-7_14

14. More About Conflicts

Mariot Tsitoara1 
(1)
Antananarivo, Madagascar
 

Last chapter was intense, wasn’t it? We talked about what are merge conflicts and when would they happen. We also saw how to resolve them manually. Don’t worry, this chapter will be much easier to digest. We are going to talk about how to get push your branch to remote after a merge conflict. Also, we are going to see some strategies to adopt to reduce the number of conflicts that might happen. Let’s go!

Pushing after a conflict resolution

As we saw in the earlier chapters, pushing means copying our local commits to a remote branch. This means that every commit we have on local will be applied on the remote repository.

We saw in the last section that a pull action is just two actions executed one after the other: a fetch action that copies the remote branch into a temporary location and a merge action that merges the temporary branch to the local one. And since the pull and push actions are just the same but in different directions, it works the same way for pushing your local branch to origin.

So a push action is divided into two parts too: copy of your local branch to remote and the merge of the branches. The only difference between push and pull actions is just a matter of which actor performs the action: you or the server.

Under normal circumstances, the push goes smoothly as the merge is performed automatically using “fast-forward.” Fast-forward is possible when the commits on your local branches can be linked directly with the commits present on the remote branch. For example, simply adding commits one after another on our master branch (like we’ve done until now) and then pushing them results in a fast-forward merge, no need to create a merge commit.

In our situation, this will happen as well as we only added new commits on our develop branch. And we won’t have any problem unless we or someone else went in the past and changed history. Never attempt to do this.

That said, let’s push our develop branch using the usual command.
$ git push origin develop
As expected, we will have the usual result shown in Figure 14-1.
../images/484631_1_En_14_Chapter/484631_1_En_14_Fig1_HTML.jpg
Figure 14-1

Pushing our develop branch

In conclusion, pushing a branch back to origin after pulling and merging the changes shouldn’t result in an unexpected behavior. Unless someone changed history.

Review changes before merge

Before attempting any merge, the most important thing for you to do is to review all the changes that your branch will introduce. It’s a crucial step that shouldn’t be ignored because it will save your countless hours of battle against Git.

Check branch location

The first thing you need to make sure is your location. To merge two branches together, you must have the target branch checked out. For example, if you intend to merge develop into master, you would need to check the latter out first. So, the code would be (don’t actually execute the second command now):
$ git checkout master
$ git merge develop

Review branch diff

Reviewing diff is not reserved for commits only! You can also use it to check differences between two branches, which is very handy in delicate situations like merging. The command is fairly simple:
$ git diff branch1..branch2
Note the two dots between the two branch names. This will show the differences between the two branches in a familiar diff view. Let’s compare develop to master:
$ git diff master..develop
The result is very similar to our diff result when comparing commits. Check Figure 14-2 for such an example.
../images/484631_1_En_14_Chapter/484631_1_En_14_Fig2_HTML.jpg
Figure 14-2

Differences between branches

If you made a lot of changes and don’t want to scroll too far, you can also view those changes on GitHub. Just push the branch and open a Pull Request!

Understand Merging

We’ve already seen many concepts about Git Merges, but let’s review them to get a clearer view of this feature. As we saw earlier, merging is the act of combining two branches or, more correctly, pouring a branch into another.

Branches can be formed from any other branch, and when a branch has been created, it becomes independent from its parent. Changes done to either branch won’t affect the other, until it’s time to merge.

Let’s imagine a situation where you create a child branch and made commits on that new branch. When the time to merge comes, several situations can arise.

If the parent branch didn’t change (no commits were made) and you attempt to merge, a “fast-forward” merge will occur. A “fast-forward” merge is technically not a merge but just a reference change in Git. Remember that Git commits behave like chained lists, meaning that a commit contains a reference to the previous one. In fact, if the parent hasn’t changed, Git just moves the reference to the parent forward (following the chained list), and the last commit in the child branch becomes the last commit of the parent branch. To put it simply, Git just appends the commits in the child branch to the parent branch. This is the easiest type of “merge” but also the most uncommon unless you work alone.

In contrast, if the parent branch has been changed (received commits), a fast-forward merge is not possible. What will occur is called a “true merge” or a “three-way merge.” This is the type of merge that we’ve seen last chapter. This type of merge will create a new commit that has all the changes in the child branch and append that commit to the parent branch. This commit is called a “merge commit,” and it has two parents: the parent and the child branches. If different commits from the parent and the child branches modified the same line of code, a conflict arises, and the developer must manually choose which changes to keep.

So, merges are just a fancy way to create commits containing all the changes in a child branch and appending it to the parent branch. It’s very important to have a clear idea of it so we can reduce the frequency of merge conflicts.

Reducing conflicts

We saw last chapter that resolving conflicts can be painful and can also take a lot of time depending in their sizes. So, it will be beneficial to us to reduce their appearance to a minimum. We are going to see in this section the strategies to adopt to limit conflicts.

Having a good workflow

Most of the problems you will encounter in Git and GitHub can be avoided if you use a good workflow. We’ve already seen in the previous chapters the most common Git workflow but let’s review it again.

The first thing to remember is don’t commit directly to your main branches. To put it simply: every change you intend to introduce into your master or develop branches should be done by merging. And each merge must be introduced by a Pull Request. This way, you can receive feedback on your work as you work on it. It also gives testers a better way to track project changes. You should always use PRs to introduce changes in the main branches even if you work alone. This will provide a much clearer and cleaner history log of the project than simple commit messages.

Each Pull Request should have the resolution of an issue as a goal. Thus, a PR should do only one thing, be it a bugfix, a feature proposal, or documentation changes. Don’t be tempted to fix several issues with a single PR. Do-it-all Pull Requests are the perfect recipes for merge conflicts.

One thing often overlooked by developers is line endings and file formatting. As we saw in Chapter 2, different OSs use different line endings. It is necessary for your team to discuss which ones to use for each project; most teams use Unix-style line endings so Windows users should configure their Git client accordingly. As for formatting, it is up to your team, but the only rule is that you must all use the same format for indentations and line returns.

Caution

Things might get heated when discussing tabs vs. spaces. Prepare your arguments in advance.

Aborting a merge

Many of your merge conflicts won’t come from code clash; many will come from formatting and whitespace differences. For example, a trailing return space or the number of indentation spaces can introduce conflicts even though the code hasn’t changed.

When confronted with these kinds of conflicts, the best move is just to abort the merge, roll back your formatting differences, and then try to merge again. As you saw earlier, the command to abort a merge is
$ git merge --abort

This won’t destroy any of your commits, it will just cancel the merge and you’ll stay at your current state.

Using a visual Git tool

When using a simple text editor, it might be difficult to resolve a conflict because most of the time, it messes up the code color scheme. A simple solution to that it is using specialized tools for Git. They can be IDE extensions or tools especially made for Git. Let’s discover them in the next chapter!

Summary

This chapter was a simple reminder of what are merges and how are they used. We saw the various types of Git merge and the situations where they can appear. We also reviewed how a merge works and what is the goal: pour commits from a branch to another.

The main things to remember are the various ways to reduce merge conflicts. You may never get rid of them, but following those advices will keep their appearances to a minimum.

We’ve made a lot of progress in our Git journey, but we’ve done it using our plain and boring consoles. It’s time to put more color in our Git projects so let’s learn about Git GUIs!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.23.101.60