Git Rebase inside IntelliJ IDEA

Rebase is one of the most awesome commands in git. Use it to keep the local branch up to date with what’s going on in trunk. A rebase a day keeps merge conflicts away!

In the form advocated here, rebase takes your branch’s local changes and applies them to the most recent code in the master branch. This makes git look as if you made your changes to the latest code, instead of to older code. Everyone else’s changes are incorporated into yours without an explicit merge commit.

Frequent rebasing reduces merge pain by catching conflicts early and in small quantities. Nothing can eliminate merge pain entirely. When conflicts do come up during rebase, git doesn’t make it clear what is going on. To help with this, I turn to my favorite IDE, IntelliJ IDEA. Unfortunately IntelliJ doesn’t make it clear either.

This post steps through rebasing a working branch to bring in the latest changes from master, inside IntelliJ. The merge-conflicts support in IntelliJ is good, but beware! It is not quite what you expect, so watch out, and read on.

Before starting the rebase:
1. Commit all your changes to the local branch. If you’re in the middle of something, commit it anyway. You can tweak that commit history later.
2. Bring the central repo changes into your master branch. (Most people use “git pull.”)
3. Check out your local branch.

In IntelliJ, look in the Changes view, at the Log tab, to see the status of your repository. If it looks like this, with commits on both master and your current local branch, then this post is for you:

To start the rebase within IntelliJ, go to the VCS menu; select Git and then Rebase…

In the dialog that appears, uncheck “Interactive.” The interactive rebase serves a different purpose. In the “Onto” field, enter the master branch, the branch with other people’s changes. Click Rebase.

If there are no conflicts, well, lucky you, you’re done. It gets interesting when the “Rebase Suspended” box appears.

Conflicts! Time to resolve them. Note that “(0/0)” here has no meaning. I’ve seen that piece work in an interactive rebase, but not this one.

Once you click OK, nothing happens. This is confusing. Your git repo is in an intermediate, mid-rebase state, but you can’t tell. The secret here is to go to the Changes view, and look at the Local tab. Now we see the files in conflict.


Caution: don’t walk away
Don’t leave your repo in the middle of a rebase. You’ll be confused as heck when you return to it later and aren’t on any branch. Either finish the rebase or admit defeat with VCS -> Git -> Abort Rebase.

To resolve the conflict, right-click on the file, choose Git, and then Resolve Conflicts. (Anybody know if there’s a handy button for this?)

Now watch out! The “Files Merged with Conflicts” dialog looks familiar from my subversion days, but the column headings are deceptive. “Yours” and “Theirs” don’t mean what you think they do. Click on Merge to look closer.
The Merge window appears. Look closely at the “Local Changes” side and the “Changes from Server” side – they’re the opposite of what you expect. The Local Changes were made on the master branch, and the Changes from Server were made in your local branch. This is the opposite of what you would expect in a merge, even a git merge. The rebase has the changes backwards.

Technical details
This happens because of the way rebase works in git. Rebase starts from the tip of the “onto” branch, master. Then it applies the localFeatureBranch commits there – so in git’s mind, we’re starting from master (“Local Changes”) and bringing in the localFeatureBranch commits (“Changes from Server”). In git terms, the master tip is ORIG_HEAD and the local branch is MERGE_HEAD. It would help if the column headings in IntelliJ were customized to reflect this.

Now that you know which changes are which, merge them together and then save. You might see this “File Cache Conflict” dialog pop up. The changes IntelliJ is making through git and the changes it’s making internally are not quite synchronized. You must choose “Keep Memory Changes” in order to retain your merge!

Repeat this process with any other files in conflict.¬†IntelliJ’s conflict resolution automatically adds the file to the git staging area, which is necessary to continue the rebase. Once all conflicting files are merged and in the staging area, continue the rebase. VCS -> Git -> Continue Rebasing.

If that’s the last conflict, then nothing happens. You don’t get any sort of congratulations, you’re just back to work. Flip to the Log tab in the Changes dialog, and see the nice straight line that shows your localFeatureBranch growing out of master. All changes are now incorporated. Good job!

If there was more than one commit on your local branch, then there’s a good chance you’ll find more conflicts. The “Rebase Suspended” dialog pops again, and you get to resolve more conflicts and Continue Rebasing again.
DON’T PANIC
If at any point you decide this rebase is a disaster, choose VCS -> Git -> Abort Rebasing. 

That’s it for rebasing inside of IntelliJ, as of v11 of the IDE and the bundled Git Integration. If I had infinite time available, I’d love to write another plugin just for rebase, because it’s a very important and very cryptic operation in Git.

Oh, IntelliJ IDEA, how I miss you

I’m switching from IDEA to Eclipse for the sake of conformity, with a great deal of mourning. No, it’s not ideal, but it’s a startup and not a good time to shave a yak.

Here are some features I miss. If you know how to do this in Eclipse, please, post a comment!

* Save the file whenever it loses focus!
* JavaScript highlighting even within .vm velocity templates
* JavaScript autocompletion. It’s a dynamic language so the IDE can never be sure what properties an object will have, but IDEA comes remarkably close. It highlights known properties in one color, and underlines ones it can’t verify. Great jQuery integration.
* Automatically importing classes that are only found in one place, or classes from specific libraries. Like, I want to type List and the IDE should import java.util.List without asking me.
* Populating the filter of packages to exclude from the import suggestion list, from the import suggestion list. Not requiring a separate trip to the preferences dialog and typing in the useless ones.
* Ctrl-W. In IDEA, this widens the selection. Widen it to the word, then the expression, then the containing expression, etc.
* At the end of a line that contains an expression, command-shift-V to introduce a variable that stores the result of that expression. — solution from Moandji: Command-2 (or ctrl-2 on Windows), then L
* Static imports: from an explicit static method call, alt-space for suggestions including “static import”– solution from Moandji: Command-shift-M
* Recognition and auto-import of static methods from certain files (such as JUnit assertions)
* add a private final field; have the IDE generate the constructor or add the parameter to the existing constructor.
* switch back and forth between a source file and its test file.
* generate a test class and have it automatically go into test sources rather than defaulting to main
* When I copy a class, open the new copy. Don’t make me go looking for it.
* In the debugger, increase the font size on variables. This is possible through some deep menu options, but then it increases the font without increasing the height of the row with the text in it. Not helpful.
* Reload projects when they change on the filesystem. Eclipse sometimes opens files with an error “This resource is out of sync with the filesystem” and no option to refresh it. Sometimes F5 on the project doesn’t cut it and I have to restart the IDE. IntelliJ handles reloads smoothly, with a prompt and a quick reload.

and, to be fair, things that are handy about Eclipse:

* Opening multiple projects in one window. Our project structure was invented by Eclipse users, so instead of one project with several modules, we have several projects with tribal-knowledge-based dependencies between them. Having three IDEA windows open and switching between them was a pain.
* Make “missing @Override annotation” an error that occurs at compile-time, without a separate code-inspection step. Same with “empty method.”
* Run tests when there are compile errors elsewhere in your project, in files that won’t be hit by the test.

I’ll add to this, and update it when people point out solutions. Suggestions in comments welcome.