This is the third part in a series
Then what is a branch in Git, actually? A branch in Git is simply pointer to the hash of a commit (which will be the HEAD commit of that branch), and a name of your branch, of course. That means creating a branch in Git is extremely cheap and is almost instantous.
Now if you look back at the branch tree in Git Extensions, you will see a linear tree. (It’s not something you usually see in your working environment, but we’re new anyway.). You can see that the name of the branches and the commit message are in bold.
For the commit, it means the commit is the HEAD commit of a active branch. A commit will always point to it parent (or its parents, in case of a merge). When you know the HEAD commit, you can know how does your branch look like, down to the initial commit (which has no parent).
For the branches, it means that those branches both point to a same HEAD commit. When we check out and create a new branch (develop), it points to same commit, too.
Now let’s make a commit in our new shinny develop branch.
New commit on develop
Now we can see that we’re on
develop, the HEAD commit now points to “Explain branch and HEAD commit”, instead of “Commit something”. “Explain branch and HEAD commit” is now in bold because it’s the HEAD commit of the active branch, while “Commit something” is no longer bold because its branches are inactive.
develop is now ahead of
master, but we still have a linear history.
We don’t have to stay with develop, we can switch back to master if we want to. And then make another commit on master:
Now we have two branched branches
Our repository starts looking like what it usually is in real world. (Of course, this is still very basic history). We now have two branches and they are not in a linear history.
develop has a commit
master hasn’t. And vice versa. This is a common scenario when you work with other people: You’re on your own branch, committed something useful but not yet pushed back to the main branch, meanwhile someone was faster and pushed their commit there. To solve this, you’ll need to merge.
Now go back to
develop (by checking it own, either using Git Extensions or
git checkout develop in Git bash. You now can click on
master branch and choose `Merge into current branch.
Option to merge from master to develop
Uh oh. Something’s wrong here. This is known as a
conflict. Your branch and the other branch both have changes in same area and Git doesn’t know how to deal with that – what to take and from where. You now have an option to (or precisely, must) solve the conflict before continue.
Git Extensions will display a list of conflicted files, as we can see here:
If you select a file and choose merge, your configured merge tool will open. By default it’s KMerge. However, I am using Beyond Compare (If you have $60 to spend, I highly recommend this small software, it’s simply the best diff and compare tool available), so here’s it look like:
Merging in merge tool
You’ll have too look closely at every conflicted line, to see if you want to use a line from your (local), or their (remote), or something between the two. When you are happy with the result, save the file AND close the merge tool to signal Git that you solved the conflict. And now you can commit a merge – Git is smart enough to list the conflicted files by default (this will help in when the other ones in your team review your changes as they know where to focus – a bad merge is a possiblity):
Commit after merging
If there were no conflict between the two branches, Git would give an option to create a commit automatically. In a perfect world, your working day will have no conflicts at all and everything will be smooth and fun and easy. But the world we live in is far from perfect, so be prepared with conflicts daily, even some nasty one.
You successfully merge from
develop – your HEAD commit now is a special commit, it has two parents.
Actually I lied!
We just merged two branch locally. It’s not something we do in real world scenarios. Your colleages pushed their changes to a shared remote branch. You will hardly
checkout a remote branch, then switch back to (
checkout) your working branch, then
merge those two. You usually do a
pull can be interpreted like this: “Hey Git, get the latest changes from this branch, then merge it to my local branch, if there is conflict, let’s me know”.
To do a
pull, select your branch and then Command Down -> Pull.
Pull from remote branch
The steps will be the same as above. You can also
pull in Git bash with
git pull (which only pull from the tracked branch).
Pull with rebase
You might have noticed the option “Rebase current branch on top of remote branch, create a linear history (use with caution)”. What does it do?
When we merge, a merge commit is created on top of your current commit. If you want to change something, you have to create another commit. Nothing is wrong, but if you are a perfectionist (like me, to have a clean history), or if your team has a policy of “One commit per work item” (For easier tracking changes), you would not want that.
Pull with rebase is the solution for that problem.
It means “Get the remote branch, then apply my changes on top of that”. If you have multiple commits, each will be applied. You will have to resolve conflicts (if any) for each step.
In most of the cases, pull with rebase is a pretty safe option. You are rewriting some history, yes, but that’s your local history. You won’t screw up other people’s work because your history is changed. Of course, if you pushed your branch and someone has been using it, it’s another story.
This action can be done in Git bash with
git pull --rebase