At last! The third and final part of our git series has arrived. The first two parts of the series get you started with git and interacting with your project. They are available here:
In the final chapter of this series, we’ll explore a few of the more advanced features.
Git has a powerful feature called branching. Branching allows you to create another path of code for a project without affecting a known good set of code. Then that alternate path can be merged back into the main path of code when you’re ready. There are several good use cases for branches, including:
- Create a new branch to test out a new feature that is experimental.
- A popular approach is to have a ‘master’ branch where your production ready code is and a ‘devel’ branch where your active development takes place.
- For team projects, there could be a branch for each developer to do their work without affecting others.
- You could create a ‘pro’ branch to maintain a version of the application with a different feature set. The pro version would use the foundation of the application in the ‘master’ branch, but would have additional features. You’d likely want to make sure all the features in ‘master’ were used in ‘pro’ to avoid complex merging scenarios.
First you should determine which branch you are in using the command
sample_app $ git branch * master
The asterisk indicates your current branch. To create a new branch called ‘devel’ off the one we currently are in, simply run:
git branch devel
Running git branch again, you’ll see that you’re still in the ‘master’ branch. Switch to ‘devel’ with the command:
git checkout devel
Now you can start coding new features without touching your functional application. Note that before you can switch to another branch you need to commit your changes to the current branch – so commit often!
You can even send your branch changes up to a git server then pull them down to continue working on another computer using the git push and git pull command such as (see part 2 for more detail):
git push origin devel
When you’re done coding and testing and it is time to put your new features into production, you’re ready to merge.
Merging and Resolving Conflicts
Merging can be really easy or rather difficult. The easiest possible merge situation is when you only have two branches and only one has been changed at a time, such as with our ‘master’ and ‘devel’ setup described above.
First, switch into the branch where you want the new code to end up, which in our case is ‘master’. Then run the git merge command to pull in the other branch:
sample_app $ git checkout master sample_app $ git merge devel
Git will work its magic to compare the changes in the two branches and bring the new code from ‘devel’ into ‘master.’ Easy enough.
When you start having problems is when there are competing changes in the two branches. If you modify the same file in both branches, git has no way of knowing which is the correct and most current change you actually want to keep. If it chooses the wrong one, your application will likely break. So it will give you an error and you’ll have to resolve the conflicts manually:
sample_app $ git merge devel Auto-merging conflict CONFLICT (content): Merge conflict in conflict Automatic merge failed; fix conflicts and then commit the result.
I am partial to using vim for editing and there is also the vimdiff tool that provides the same kind of interface for resolving conflicts. It breaks up the screen into three different sections:
- Local – A temporary file containing the contents of the file on the current branch.
- Merged – A screen where you perform your resolution that will ultimately be written out as the final conflict resolved copy to be committed.
- Remote – A temporary file containing the contents of the file to be merged.
To start the resolution, run
You’ll be prompted for the file with a conflict and asked to choose a resolution tool. Choose vimdiff and you’ll be presented with the three screens as outlined above. Which one is which, you ask? To find out, run the vim command :ls and it will show you local and remote with the remaining being your working screen. Press enter and your cursor will be positioned in the working screen on the first conflict.
Here is where things can get tedious if you have many conflicts to resolve. You need to review each problem and make your choice of Local or Remote. If you really need to fix something, you can even add your own changes that don’t exist in either Local or Remote.
Navigate the conflicts and make your selections for resolution using these commands:
- ]c – move your cursor to the next conflict to resolve
- [c – move your cursor to the previous conflict to resolve
- :diffget # – choose which screen to pull in to the resolution and use. Here, # is the number of the vim screen partition you want to grab from. So to grab from the left screen, you’ll do :diffget 1 and to grab from the right you’d do :diffget 3. There is also the diffput command, but I do not typically use it as it is more confusing for me to move around in the different screen partitions rather than stay on the one and only use diffget.
When you are satisfied that all the conflicts have been resolved in your middle screen, run the command :wqall to write all files and quit. Then commit regularly with git commit and you’re done!