I recently started working in an environment where we have to develop software using teams that are geographically dispersed over several timezones. There are many challenges (and benefits) of such a set up. The one I will address today is to do with team collaboration and source control.
I have been a user of Subversion and the Atlassian suite of tools for some time now but discovered that when it comes to distributed teams, JIRA and Cruicible fall short. One obvious shortcoming is the inability for team members to do code reviews without checking in un-reviewed code into Source Control. I will use this as the basis of making a case to switch to a Distributed Version Control System, and in doing so, will describe how to use Git.
Git is complex. But distributed version control is a complex problem so it is understandable that the internals of Git makes you feel like you should have a PhD in Graph Theory. However, in true Agile fashion, I approached the problem of understanding Git with a User Story in mind. And my User Story is the following:
“How can I use Git to allow and enforce peer-code reviews, maintain a couple of branches of the product and not have to remember hundreds of Git commands on my finger tips”
Of the several of ways to choose from, to achieve the above, here’s one possible way that worked for me.
First I will describe the overall approach and then discuss each step in detail.
- Use EGit to create a local repo and to switch and create branches.
- Use GitHub to host remote repository (It’s free)
- Use EGit to push a feature branch to the remote repo
- Use GitHub to send out Pull Requests to potential reviewers based on the push.
- Use Github to view Pull Requests that already exist.
- Use EGit to confirm that the changes in the pull request are good and to merge into master locally
- Use EGit to push the master to remote.
As you can see, I’ve used EGit, an Eclipse plugin for the Git client and GitHub.com for hosting a remote repository. This choice has two benefits:
- Keep away from the command line interface of Git with it’s formidable array of commands and options.
- And the bigger reason: Get introduced to GIT best practices that these tools default to.
Steps:
- Get EGit. This is an Eclipse client for Git and can be downloaded as a plugin by using this update site. Note, that there is no need to download and install Git itself.
- Create a project in Eclipse and add a few files to it.
- Click on that project and go to Team -> Share and select Git as the SCM provider and then create a new repository. Note that you can also clone an existing Git repository, which typically means that you can “copy” a remote repository to your local environment. But we will start upstream of that event just for completeness.
It is stated in EGit documentation that it is not a good idea to create a Git repository in the eclipse workspace. Since I am not totally convinced of the reasons given, I will create a directory in my eclipse workspace called gitprojects and create the repository within that (YMMV).
- Press Create to create a new repository. Git allows us to have as many code repositories as we like. If you have used the git clone command for downloading an open source project, you have essentially copied an Git repository to your local machine. One nice feature of Git is that it doesn’t clutter up each directory of your code base with dot directories (hidden directories) like Subversion does. But it does create one .git directory at the top level of your directory structure. This .git directory represents a repository, not a project. You can have as many projects as you like within that repository.
- Make sure that you have added gitignores by going to Team|Ignore. Then ensure that you also check in .gitignore for others to use. If, you goof up and ignore .gitignore, you can simply edit the .gitignore file and delete the /.gitignore entry. A far sight better than SVN’s handling of svn-ignores!
- Now let us do the initial commit of our project like so:
- By doing all above, you will have a local project and it’s shared to your local Git repo. Now go ahead and make a change to a file or files. By doing so, you are working on your local master trunk. No branches yet. Now, I’d like to find out what I’ve changed. For that, I can simply do a Team|Synchronize View which will show me the diffs between my workspace and my local Git. Go ahead and commit the changes, just like you would, in any source control system. Just to make it look real, make another change (like adding a file) and commit again.
- Now let’s say that you need to start working on a User Story called Add Validation. In the SVN world, you would have continued to work in your synched up workspace to make changes by say, modifying a file and adding another. But not so in Git. Here is where we start to use Git’s cool feature of branching. First confirm that you are on the master branch on your local like so:
Next, select Team|Switch To| New Branch to get the dialog shown below
Notice that we are branching off of the master. We see that the current ‘head’ of our project has changed:
So now we can start to develop in the for-validation branch.
- This time, you can modify the United.java class and add another class called Validation.java. And check that in. So now you have a commit on the new branch. Now the interesting part. Select Team|SwitchTo| [master | for-validation] and see the code in your workspace switch. That is pretty neat because by merely changing a branch, an entire new code base is swapped in, including added and dropped files.
- At this point, I would like to visually see the changes I have made. So I go to Team|Show In History. The view shows me two commits on the master branch and then one on the for-validation branch.
- However, to see true branching behavior, let’s start working on yet another story for say, changing-theme. But you don’t want to make that change on the validation code you just added. So we will create another branch off of master by going to Team|Switch To|master and then Team|Switch To | New Branch. This results in a dialog as below:
Note that the source-ref represents the place you branch out from. Since we want to branch from before we started working on for-validation, select master as your source-ref. Specify a new branch called for-world-peace. By doing so, eclipse (EGit actually) will implicitly check out the new branch and make that current (aka head). - Make changes on that branch (to implement world-peace), commit a couple of times, so that we can see some nice nodes. Now let’s see how it looks in a graphical representation by checking out the history:
Here we see two commits off of the for-world-peace branch.
- Next I want to have someone review my changes that I have made on the FEATURE-123-Use-Validation branch. To do that I will need to push that branch over to github and issue a pull-request to all contributors of the repository.
But I do not yet have a repository on github to which I can push. So I can go over to github, log into my account and create a “New Repository”. In the case of a corporate repository, this step will not be necessary as, hopefully that repository will already have been created by your company admin, and you would have been given access to it.
Once you have created that repository, you can cut-n-paste it’s URL and then in your local Eclipse, go over to Team|Remote|Push and enter it like so:
Upon hitting “next” I see the following dialog that basically askes me to map my local branch to a remote branch (defaulting to the same name):
Finally getting:
Hop over to github.com to see if your files actually made it… they should have. - On github.com, you can now switch over to the just checked in branch (from a drop down) and issue a pull-request to the committers of the repository.
- Let’s switch roles now, and assume that you are the one reviewing the pull request. Github will give you nice tools to do diffs on files modified and you are able to add comments to each file. After this interaction with the developer, let us suppose that you are ok with the changes and would like to merge feature-123-use-validation to the master branch on the remote.
The point to note is that the merge will be done on a local developer’s machine and then pushed back to remote. - Go to Team|Remote|Fetch From.. and fill in the following dialog
Here we are pulling the remote master into the local master branch.
- Check Team|History to ensure that the picture of the repository looks good. Ensure that the current branch is master and merge feature-123-use-validaton into it.
-
Select for-validation and hit ok. There are no conflicts here so we have a clean merge and GitX displays as below:
As we can see, master and feature-123-for-validation now sit at the same head. - Now let’s merge for-world-peace and master. Do the same thing, switch to master (if not already there) and merge for-world-peace into it.
Here we see a slight problem. There is a conflict because the same file was modified in the two branches.
Correspondingly, Git modifies the file(s) in conflict by showing the following diff (The stuff above the ===== is in the head, the rest is in the branch being merged
public class SouthWest { <<<<<<< HEAD //A line to suport validation ======= //To support worldPeace //Some more world peace >>>>>>> refs/heads/for-world-peace }
So fix the file as you see fit and then select “Add to Index” from the menu below.
Follow the above action with a commit so that changes are actually merged. At this point we have merged feature-123-use-validation to the master locally. Ensure that Team|Show In History shows the right branches
- Now it’s time to push the master to the remote repository so that it can be subsequently built and deployed (by a Continuous Integration tool, say). Use Team | Remote|Push to push the master branch to the remote much like we had pushed the feature-123-use-validaton branch earlier on.
Gotchas:
- When accessing a remote repository via EGit you may see:
“Exception caught during execution of ls-remote command”I found that this happens if you have NOT used EGit’s project import wizard to pull in the project initially from the remote repo. (Instead, you may have used the command line option of git clone <url>)
To fix, this, if possible, use File|Import|From Git … and get the project that way.
Now, if you go to Team|Remote|Fetch From… you will be able to complete the dialog that would otherwise issue the error above. - EGit, as of May 2012, doesn’t support the git stash command. That’s a bummer, because stash is a very useful thing to have around because of the maddening “feature” that does not allow one to switch branches without committing changes that you may have currently made. To get around that, since there is no EGit equivalent, I had to resort to the cli
- git stash
- git checkout the-other-branch
- git stash pop
Now you will have all your changes in the newly checked out branch.
What’s JGit
JGit is the pure Java implementation of Git (which is written in C, Perl, shell an Ruby(?)). EGit is an Osgi plugin for Eclipse based on JGit.
Resources:
- EGit: This is a comprehensive EGit guide: http://wiki.eclipse.org/EGit/User_Guide#Getting_Started
- The Git Book (I found Chapter 5 is especially very useful where usage scenarios are discussed): http://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project