A better Git workflow with IntelliJ

We at Sandstorm use IntelliJ as our IDE for almost every kind of project or technology. It's a swiss army knife for developers. I really fell in love with this software. You always discover new features that makes life easier and coding more fun.

I recently was messing around with the git integration of IntelliJ. Although I've been using git for years I have to admit that I still hesitate using the CLI. Whereas committing, pulling and pushing isn't a problem from the console, I always find myself googling for the right commands when it comes to stuff like rebasing, cherry-picking or solving merge conflicts. So far my GUI of choice was Atlassian's SourceTree with Xcode's FileMerge integration. One problem with using a separate tool for VCS is that it's not immediately up-to-date after file changes that you save in another editor. As I discovered IntelliJ's fancy Diff view I started investigating on how I can use git from within to avoid switching between the IDE, the Terminal and SourceTree.

Nifty tooling

The first cool thing you'll notice after enabling version control for a project ist the git information on the bottom right corner (Something like Git:master). It tells you which branch you're on and if you click it a small menu pops up with a list of your local and remote branches. You can easily switch between branches, checkout new branches, compare them, merge them, delete them and so on. Nothing special so far.

One handy feature (that is hard to do with a CLI command) is the Annotate view: it displays -per line-  the date and author of the according commit that the line last changed. How cool is that? Just right-click the line-number to see who you can ask when you think "WTF is this line doing here?" ;-)

The basic git stuff is easy to find on the VCS menu and you'll quickly get used to the shortcuts once you use them: ⌘+T for a pull ⌘+K for a commit and ⌥⌘+K for a push. There is also a version control panel that you can open from the tool buttons of the bottom bar. It has tabs for local changes, the git log and a console where you can see the actual git commands that are executed in the background. Of course you can execute your own commands right there. Depending on what you are doing you'll see other tabs in that panel like a changelog after pulling from origin.

Spotting the changes

What I find very useful is the ability to visualise changes between different versions of a file or a project. You can display what changes appear between the master and a feature branch or how the current file differs from another branch or commit. This is important when organising your local workspace, like separating your current changes into different commits or removing some console.debug statements before a commit. Especially when you have to checkout different branches very often, e.g. for reviewing pull requests.

Conflicts can be annoying. And I think a good merge visualisation can make it easier to understand how to resolve merge conflicts. As mentioned, I used FileMerge before. I thought resolving conflicts couldn't get easier. But once I was familiar with doing it within the Diff view of IntelliJ I could not imagine doing it manually again. Let's say you have some local commits that you are about to push but you first want to pull with a rebase strategy. Boom! Conflict! In the "Resolve Conflicts" Dialog IntelliJ will show you the affected files and you can either choose your or the other version straight away. But if you're not sure which version is correct or some adjustments must be made then you can merge it within the Diff view. In a split view with three panes you get both versions on each side and the merge result between them. Changes and conflicts are highlighted. Now you can decide how to resolve conflicting changes, maybe also edit in the result pane and auto-merge non-conflicting changes. There's now also a new magic resolve feature which tries to do that job automatically. If you try it, you should double-check the result. Anyway, after all conflicts are resolved, save and continue rebase and you're done!

Mastering the history

In the commit dialog you have the opportunity to amend your commit to the last one. You can also check and uncheck which git hooks you want to run before and after the commit. IntelliJ offers some useful hooks like code formatting, code analysis or checking TODOs in the code.

Sometimes you have to do crazy things with your git history - Interactive rebasing. This can be a mess. You maybe know this from other git tools. This is well solved. Select your branch and the base which can be another branch or a commit, then pick, rename, squash commits as you would do it anywhere. It just looks nicer. But I somehow find that a GUI can make it easier to conceive visually what you are doing. And because rewriting the history is always dangerous, I find that very important.

More power with plugins

By default, IntelliJ comes with a Github plugin. As we use Gitlab for repository management I've installed an appropriate plugin for this. Now I can create my merge requests straight out of the IDE which actually works really great. You can also list open merge requests, review, comment and merge them right there. I haven't tried other plugins but I'm sure there are some other cool helpers out there, like listing issues and so on.

I've probably only seen the tip of the iceberg but I can't wait to discover more features.

UPDATE: Sebastian just pointed me to a missing feature that I didn't notice yet (I'm using git in IntelliJ for just a couple of days now). There is indeed one thing missing which I really like in SourceTree. You can not add partial changes ("hunks") to the stage or discard them. Or vice versa, discard hunks. SourceTree lets you even select single lines of changes to stage / discard them which is really nice. I've seen a ticket for this and hope they'll ship this feature with one of the next versions of the git plugin.