Week 4Day 5 - "Conflicting information"
What to do when it all goes wrong
The team have been playing with branches for a few days now and are beginning to settle into the idea of branching often.
As you can see, in Git, a branch is a really simple device for allowing experimentation and development.
As the implementation of branches is so simple, it is also really fast to switch between branches.
If you have been keeping up with the After Hours sections, you can hopefully see that when switching branches, Git only needs to alter what is different between one working copy and another.
Let us see what new issues the team runs into during Week 4.
In the trenches...
"Hmm," the puzzled noise reverberated round Klaus' corner of the office.
Mumbling commenced.
"So if I did that, then why is that not ... I mean I didn't think ... that's probably the problem Klaus ... sill 'luser'."
He chuckled to himself, hardly noticing the looming figure of John.
"You got a conflict," said John matter of factly.
"I can see that," came Klaus' reply.
"The problem is how do I solve it"
"What were you doing?"
"Well I had a few branches I had been working on and I was trying to merge them in."
"Figures," said John.
Conflicts are bound to happen at some point.
Even the best project processes in the world will sometimes end up with a conflict that has to be resolved.
We should, at this point, clarify what a conflict actually is.
A conflict occurs when an attempt is made to reconcile multiple changes to the same section of a file at the same time.
This usually means that the same line of code is modified by two different parties at the same time.
Now that we have several branches in our test repository, let us merge them back into master and see if we come up with a conflict.
First we are going to add a little change to one of our files.
john@satsuki:~/coderepo$ echo "Another update to this file" >> my_third_committed_file
john@satsuki:~/coderepo$ git commit -a -m 'Updated third file'
[master 4ac9201] Updated third file
1 files changed, 1 insertions(+), 0 deletions(-)
john@satsuki:~/coderepo$
Now let us merge in our wonderful branch.
john@satsuki:~/coderepo$ git merge wonderful
Merge made by recursive.
newfile1 | 1 +
newfile2 | 1 +
2 files changed, 2 insertions(+), 0 deletions(-)
john@satsuki:~/coderepo$
So far, our merge has gone pretty well.
We have taken all of the changes that were made in the wonderful branch and pulled them into our master branch.
If we wanted to, we could continue on making changes to the wonderful branch and we could keep pulling changes in from one to the other as time went by.
Figure 5 shows what our repository looks like now.
|
|
Figure 5 Repository state after wonderful merge |
|
Our repository tree looks a little strange now.
Notice that the most recent commit, b119573, has two parents.
Can this be true? If we run our git show command against that commit ID, we see something new.
john@satsuki:~/coderepo$ git show b119573
commit b119573f4508514c55e1c4e3bebec0ab3667d071
Merge: 4ac9201 cfbecab
Author: John Haskins <john.haskins@tamagoyakiinc.koala>
Date: Fri Apr 1 07:35:13 2011 +0100
Merge branch 'wonderful'
john@satsuki:~/coderepo$
Notice the line Merge: 4ac9201 cfbecab , which simply tells us that this is a special type of commit, a merge commit.
When we previously performed a merge, the branch we were merging back into had not changed since we branched off and did our development work.
If you remember, we called this a fast-forward merge, simply because we moved our destination branch forwards to meet the tip of the development branch.
This time things are a little different.
During out development time on wonderful, something changed on master.
We did not make many alterations, but just the fact that we made some changed the way the merge was performed.
In fact, we can see this if we look at the first line after the merge.
Instead of indicating a fast-forward merge, it now states that the merge was recursive.
If you are interested in the different types of merge algorithms, you should read the After Hours section for this chapter.
So now the history of this latest commit actually relies on two previous commits.
The changes that have been introduced in these commits are merged together to form a combined tree of files.
This is the snapshot that is represented in commit ba0bfc.
Let us now remove that third file before merging in the zaney branch.
john@satsuki:~/coderepo$ git rm my_third_committed_file
rm 'my_third_committed_file'
john@satsuki:~/coderepo$ git commit -a -m 'Removed third file'
[master ed2301b] Removed third file
1 files changed, 0 insertions(+), 2 deletions(-)
delete mode 100644 my_third_committed_file
john@satsuki:~/coderepo$
So now we are ready to merge in the zaney branch.
john@satsuki:~/coderepo$ git merge zaney
Auto-merging newfile1
CONFLICT (content): Merge conflict in newfile1
Auto-merging newfile2
CONFLICT (content): Merge conflict in newfile2
Automatic merge failed; fix conflicts and then commit the result.
john@satsuki:~/coderepo$
Oh dear! That probably did not go as well as we had hoped.
We have two conflicts.
The first is in newfile1 and the second in newfile2 .
You may be wondering why.
The last commit we made did not make any changes to either of those files, so why do we suddenly have a conflict? When using a version control system like Git, conflicts are an every day part of life.
A conflict does not mean you have done something wrong.
It simply means that the automatic merging algorithm can not merge the changes.
In this case we have a conflict because the files have changed in both sides of the merge, since they diverged.
Rephrasing this slightly: Since the point when we created the branch zaney, at commit 55fb69, both the master branch and the zaney branch have both modified the same files in the same places.
Right now, our merge is paused.
For now, let us abort the merge so that we can take a closer look at what happened.
john@satsuki:~/coderepo$ git reset --merge
john@satsuki:~/coderepo$
So let us compare the changes between our latest common ancestor and our branch tips.
We will first look at the difference between the ancestor and our master branch, but limit it to the files with conflicts.
john@satsuki:~/coderepo$ git diff 55fb69 master -- newfile*
diff --git a/newfile1 b/newfile1
index 24e7dfa..ef20984 100644
--- a/newfile1
+++ b/newfile1
@@ -1 +1,2 @@
A new file
+and some more changes
diff --git a/newfile2 b/newfile2
index cba16cc..dac4357 100644
--- a/newfile2
+++ b/newfile2
@@ -1 +1,2 @@
Another new file
+and a new feature
john@satsuki:~/coderepo$
Now we will do the same between the common ancestor and the tip of zaney.
john@satsuki:~/coderepo$ git diff 55fb69 zaney -- newfile*
diff --git a/newfile1 b/newfile1
index 24e7dfa..d94dacc 100644
--- a/newfile1
+++ b/newfile1
@@ -1 +1,2 @@
A new file
+and some awesome changes
diff --git a/newfile2 b/newfile2
index cba16cc..45659d7 100644
--- a/newfile2
+++ b/newfile2
@@ -1 +1,2 @@
Another new file
+and some more awesome changes
john@satsuki:~/coderepo$
So hopefully you can see that we have tried to change the second line in both files.
This is where the conflict is arising from.
Let us try to run the merge again and this time we will resolve the conflicts manually.
john@satsuki:~/coderepo$ git merge zaney
Auto-merging newfile1
CONFLICT (content): Merge conflict in newfile1
Auto-merging newfile2
CONFLICT (content): Merge conflict in newfile2
Automatic merge failed; fix conflicts and then commit the result.
john@satsuki:~/coderepo$ cat newfile1
A new file
<<<<<<< HEAD
and some more changes
=======
and some awesome changes
>>>>>>> zaney
john@satsuki:~/coderepo$ cat newfile2
Another new file
<<<<<<< HEAD
and a new feature
=======
and some more awesome changes
>>>>>>> zaney
john@satsuki:~/coderepo$
We have displayed the contents of newfile1 and newfile2 during this merge.
At the moment, no commits have been made and we have a chance to tell Git exactly what these files should look like.
You should be able to see that Git has modified the files to show us what both the branches think the file should look like.
At the top, just after the <<<<<<< HEAD is how the line was modified in the master branch.
We then have a divider ======= and after that we have the line, as it was modified in the zaney branch, followed by >>>>>>> zaney .
We can now edit these files manually, remove the dividers, header and footer and commit the result.
We are going to edit newfile1 to look like this:
A new file
and some more awesome changes
We will also edit newfile2 to look like this:
Another new file
and a new awesome feature
Now we have resolved the conflicts and set the files straight, we can add the files and commit the result.
john@satsuki:~/coderepo$ git add newfile*
john@satsuki:~/coderepo$ git commit -a -m 'Merged in zaney'
[master d50ffb2] Merged in zaney
john@satsuki:~/coderepo$ git log -n2
commit d50ffb2fa536d869f2c4e89e8d6a48e0a29c5cc1
Merge: ed2301b 7cc32db
Author: John Haskins <john.haskins@tamagoyakiinc.koala>
Date: Fri Apr 1 07:42:04 2011 +0100
Merged in zaney
commit ed2301ba223a63a5a930b536a043444e019460a7
Author: John Haskins <john.haskins@tamagoyakiinc.koala>
Date: Fri Apr 1 07:37:34 2011 +0100
Removed third file
john@satsuki:~/coderepo$
Our merge has been completed.
Let us now see what our repository looks like graphically, in Figure 6.
|
|
Figure 6 Repository state after zaney merge |
|
It is quite reasonable that you may want to continue working on branches after you have integrate those changes back into your master.
Obviously you would have to merge your master branch back into zaney or any of the others, otherwise you could be developing on older versions of the code.
This brings up an interesting point which will be covered in a later chapter, as there are multiple ways to keep a development branch up to date.
So, at the end of this week, we have learnt how to create branches, how to merge them, and how to resolve conflicts.
It should be stressed that a conflict is not a bad thing.
Conflicts do happen and when they do you should simply take your time and resolve the conflict in order to represent the best of both worlds.
The After Hours section for Week 4, takes a closer look at merging and explains some more about what branches are at a repository level.
|
|