What is this all about?
At JBoss Tools we use git when it comes to source control and have all our components hosted on Github. Eclipse offers very capable and handy git tooling with EGit. Nevertheless most of us still use the command line even though we do and use Eclipse on a daily base. The main reasons are manifold. EGit was for a long time still on the maturing curve and was not on par with the command line. Migrating users did not feel safe either since EGit was not operated in the way it used to be with CVS, SVN etc. Furthermore many UIs in EGit dont try to accomodate the newbie user but offer all the nots and bolts instead.
I'm using the EGit/JGit API (not the UI) in our OpenShift tooling. EGit/JGit is thus thus vital for my product. I therefore started to eat my own dogfood and forced myself to use EGit where I had the reflex to switch to the command line before. I found a pretty mature and very capable EGit. It is IMHO even more handy than the command line in certain usecases. I'll therefore try to improve EGit and convert most of us to us to use what was an unloved child before. I am very convinced that a little effort here and there will tranform EGit into first class and most excellent tooling that noone wants to miss.
A tale of branches and main repos.
As I already mentioned, all JBoss Tools components are available from Github. All developes within JBoss Tools have their very own fork of those master repos and also have them on Github. When it comes to issue tracking, we use Jira. Whenever we tackle an issue we branch from master and commit our work to this topic branch. Since git is decentralized, one would have a local clone of its Github fork, commit the changes to it and push it back to Github. A pull request then notifies the maintainer of the master repo to review the suggested changes and merge them into the master branch in the master repo.
Clone your Fork
Everything starts by having a personal fork on github. Once this is done one clones this repository to the local machine by issuing (I'm cloning my fork of the openshift-java-client here):
git clone git://github.com/adietish/openshift-java-client.git
In EGit things are as easy as on the command line. You make sure that you have the Git URI in your clipboard and switch to the Git Repository Exploring perspective...
You pick the Clone a Git Repository action from the toolbar in the Git Repositories view...
... and EGit will use the Git URI in your clipboard to prefill the upcoming cloning settings:
In an additional step you tell EGit where to clone to and once you're done the new repository will show up in your Git Repositories view:
A last step involves importing the Eclipse project to your workspace. You pick Import Projects... from the context menu of your repository and you're all done:
Create your topic branch
Every issue is tackled in a separate topics branch. That allows us to separate the concerns when coding. It also allows us to switch among different task if priorities shift. We simply switch topic branch and focus on the current task.
To create a new topic branch based on the current master branch one does:
git checkout -b my-topic master
Using EGit you wont have any more steps involved, it is as easy as it is with the command line. In the Git Repository Exploring perspective you unfold the branches of your local git repo, select the branch you want your topic branch to be based on (which usually is master) and pick Create Branch... in the context menu.
In the upcoming dialog you choose the name of your new branch and hit Finish. EGit will by default switch your code to the new branch (see Checkout new branch checkbox).
Do your work
Now that you have a branch for your task you're ready to do your changes. Get to the Java or JEE perspective and make sure you have the Git Staging view opened. It will track the files you change and make them show up in the Unstaged Changes. If I change the README.md in my project and save it will instantly get listed in the Unstaged Changes:
Commit and Push
As soon as we're done with all our coding we're ready to commit and push. When we started we cloned our Github fork to the local machine. Our local repository is thus originating from the Github fork, it's Origin is pointing to this Github repository. Committing and pushing our changes to this fork is done in 2 steps when using the git command line:
git commit -a -m "[JIRA-XXXX] corrected readme"
git push origin my-topic-branch
In EGit things are even more handy. it's mostly a single click experience:
PIck the unstaged files that you want to commit and drag and drop them to the Staged Changes. Then provide some commit message and you're ready to go:
Hit Commit and Push:
EGit will commit your changes to your topic branch and push it to its origin, the fork on Github. The topic branch only exists on the local machine, pushing it to Github will thus create a new branch in this remote repository. EGit will show you that in the upcoming dialog:
Oh, my change was incomplete
If I now discover that I forgot to do some modifications I could easily add another commit and push it as I just did before. The commit history would then show 2 distinct commits that tackle the very same topic though. I personally prefer to ammend in these cases. I could alternatively merge these 2 commits into a single one (by rebasing). I personally prefer to amend since this keeps me focused, I wont have to rebase once I'm ready to merge.
Let us do the change we had forgotten and amend it. On the command line this would look like this:
git commit -a --amend
In Eclipse I'll proceed as shown before. I change my files, drag them from unstaged to staged and tell EGit to amend by htting Amend Previous Commit:
When hitting Amend Previous Commit the previous commit message shows up in the message area.
Also Notice the warning that the Git Staging view is issuing. It tells you that your current commit was already pushed to a remote branch. It tells you that pushing will most likely fail if done to the very same remote:
Doors shut, push rejected?
If I now hit Commit and Push committing will succeed but pushing wont. It'll tell me that it is rejected, that it is non-fast-forward:
Amending modifies an existing commit. I had pushed this commit to my fork already. The new push sends a modifed version of the very same commit. The remote therefore rejects it and tells me that I pushed a conflicting change, a change that is non-fast-forward.
Use the force, Luke!
To solve this conflict I'll use the force. I changed my commit on purpose, I wanted to add some missing pieces. I therefore insist on pushing my change and tell git to force my push. Using the command line one does:
git push -f origin my-topic-branch
The Git Staging view unfortunately doesn't offer this option yet. We should consider ways of pushing force in the staging view in order to keep the workflow smooth.
To currently push force in EGit you have to get to the full fledged push action that is accessible in the project explorer: Team -> Remote -> Push...
In the upcoming wizard you can then choose the remote repository you want to push to. I therefore pick origin, which points to Github fork:
On the next wizard page I then pick the branch I want to push:
EGit will then pick the existing (remote) topic branch as destination:
I can then add this source-destination pair to the refspecs that EGit will push:
EGit then lists it in the push specifications and allows me to further further fine-tune it:
I can then tell it push force in the Force Update column:
Once I executed all these steps I'm ready to go and can hit Finish, telling EGit to push force my local topic branch.
In my opinion the wizard we just used - especially the 2nd page where you build a push-force refspec is far too complicated. It's not inutitive enough, especially for newbie users. The git command line is so much simpler. We should seriously think about providing a simpler version which fits the vast majority of the usecases. A very simple first improvement is to have the current branch preselected (in Source ref).
Let there be merge
Now that we pushed our topic branch to Github we're ready to file a pull request and let the review happen. Github contributed Mylin connectors for EGit/Github. I honestly didn't find out how to file pull-requests though. Looking into their code this must be possible, the functionality is there, but I didnt get how to use the UI to file those. That's another area where I'd love to enhance. IMHO it should be possible to file pull requests right from Eclipse. I'm not even sure if we really should require Mylyn. Mylyn is very nice, no doubt about But at times you just want to create the PR and dont want to edit it. Having it prefilled with the commit-message is fairly sufficient.
For the sake of breveity I wont get into details how one would file a pull request on github, I'll just assume we filed it on github.
I then get back to the Git Repository Exploring perspective and merge master with my topics branch. I double click my master branch and make sure it's checked out. I then pick Merge... from its context menu:
...and tell the wizard to merge my master with my topic branch.
Once the merge is done I can push it to master by using the push outlined above. This time I'll just push to the master repo, too and publish my code the official repo. My task is finished, I merged my topics branch (my work) into master and made it official JBoss Tools code. I can now kill my topics local and remote branches.
Egit, what about new clothes?
In the above I spotted several steps that could get enhanced to get your EGit journey perfect. EGit has all you need, it's became pretty stable and mature over the years. I'd love to gather those ideas and wishes and get your feedback!
A first shortcoming I spotted is that you cannot push force from git staging view. Maybe that's a good practice since push force should stay the exception, it should not become the general rule. It should not be too easy since there's too much you can do wrong and loose all your work.Nevertheless I'd love to allow this common practice when correcting pull-request (in github or EGit gerrit) in the Git Staging view.
- What about displaying a force update checkbox when the already pushed warning is displayed?
- Alternatively we could ask the user if he wants to push force in something similar to this:
IMHO the dialog that pops up when you want to push to some remote could get quite some love.
- First of all it pops up with no default being filled out. Having the current branch selected as source and the corresponding remote on origin (that gets selected as soon as you choose your source) would spare you 1 first unneeded click:
- Secondly the dialog is far too complex and not intuitive enough for the newbie user. There are far too many nots and bolts for the default usecase. We could offer the simple 1:1 push with a force option in a single line and hide the advanced option behind an Advanced >> button:
I might be wrong but it looks very much as if there was no way to create a Github pull request within Eclipse. One has to use hub or get to the website. I'd love to have this in Eclipse so that there's no need to switch app. IMHO we should offer this with or without Mylyn. Maybe we should offer an action in the context menu that opens the Mylyn pull-request editor prefilled with all the relevant informations that I can then just confirm.
I'd love to get your feedback, get to know what you guys think. Please shout at me, confirm me or comment with your very own ideas. Thanks!