Monday, January 24, 2011

Congratulations, it's a Gantt!

This latest version of Gigantt - the first version considered beta - has many new features and improvements. But the most important one of all is that it's the first version that actually displays your work plan along a time-line, similar to a standard Gantt chart.


Of course, there's nothing standard about this new layout. After all, it's the only Gantt viewer in the world that's lets you view Gantts recursively.



There's lots more in this version as well. But I've already posted the big feature list in recent posts, so I won't bore you with it.


If you want to try it out, just drop me a line at beta@gigantt.com and I'll create an account for you.

Sunday, January 23, 2011

Why Rewriting History Is A Good Thing

Part of the whole Git vs. Mercurial debate revolves around the issue of rewriting history.


Git lets you change your code's history after the fact, and it lets you do this very easily. Mercurial doesn't, and Mercurial people seem to think this feature isn't very important.


The misconception seems to be that the only reason to rewrite the source code's commit history is to hide the errors that were made in the past. In other words - ego. While that may be part of it - I can certainly imagine someone wanting to contribute code to the Linux kernel and wanting his patch to appear as if it was written by Jesus himself (correct from the start and with no back tracking to fix bugs) - that's certainly not the whole story.


Much more important, in my opinion, is the signal-to-noise ratio of code and code-history.


We treat code with the utmost respect. For example, it is widely considered very poor form to keep around commented out code in your source file, especially if it's obsolete or incorrect. We want the people reading our code to not be distracted by irrelevant information.


These days, the SNR aspect of code applies more and more to the code's history as well. People review each other's code and need to be able to know that the changes they're trying to review aren't just typos and mal-commits. Tremendous amount of time can be wasted by trying to wrap your head around a commit somebody else has made, only to later find out it's just a bit of nonsensical noise that was committed by accident.
It's also a great advantage to be able to keep certain branches so clean and stable that every single one of their revisions is a rock-solid snapshot that can be checked out and branched from. When you can't keep your history clean you end up defining other methods of marking commits as good/bad.. and that's just silly. Some branches cannot afford bad commits.


Ultimately, Git's approach lets you treat your code's history with the same respect you treat the code itself. Having the ability to rewrite history with such great ease also facilitates more free experimentation and makes you commit more, because you don't have to fear polluting the global history with half-baked tinkerings. Git lets you alternately focus on coding and then SCM. You make your code as good as you can, and then you clean up the history as well as you can, before inflicting it upon the world.

Monday, January 3, 2011

Eureka!

I just had a eureka moment when it comes to working with Git. I recently moved from Mercurial to Git and it took me a while before I realized that I've been working in a SVN mindset with Mercurial this whole time.
Let me explain.
I now see that it all comes down to a simple methodological decision: create a branch for every meaningful change. I didn't believe this at first when I read this idea a few months ago. Why should I create a branch for every small modification? Changes to the code-base are mostly serial, occurring one after the other and often depending on each other, so won't all this branching just create more merging headaches?

Now I see that they don't. In fact, they reduce merging. The problem was that I was thinking of merging in SVN terms - taking unrelated deltas of code and applying them to the same branch. That indeed is a pain in the ass. But it's not what merging means in Git. In fact, it's a shame that Git even uses the same word - merge. Linus should have come up with a different term. So, yes, what SVN people call "merge" almost never happens in Git, even with all these extra branches being created.

There is a simple fact that just has to sink in when you move from SVN to Git: unless you actively harm Git's history (e.g. cherry-picking revisions) merges always work.* Please digest this fact for a moment. Assume that it's true and try consider how it should change your working habits.


Let's take an example of a typical coding scenario. You work on a new feature and after you start coding for a while you notice you need to do some refactoring or introduce a new common function. In the end you decide to abort the new feature - maybe it didn't pan out as you'd hoped and you choose to cancel it. However, you do want to keep that little bit of refactoring that you did - that's just a good common piece of code that's unrelated to the feature you were working on and you don't want to throw it away along with the abandoned feature.

The wrong way to do this is to do the refactoring on the same branch as the feature. If you do this then you have to later cherry pick that delta of code and apply it to your "trunk". But that circumvents Git's history. It's like taking a patch of code from an unrelated branch and applying it. It may work and it might not. 


The right way to do this is to open a new branch for the refactoring effort and merge it to the feature branch. This is an instantaneous merge in Git - a single click that takes less time than a commit in SVN.

Now, if you want to keep the feature, you just keep working on it and merge it into the trunk in the end. If you want to throw it away, you just merge the refactoring branch back to the trunk and continue from there. In fact, both of these alternatives are trivial "fast forward" operations in Git. In other words, no real merging (in the sense you know from SVN where changes from multiple sources are applied to the same file) is taking place. Git is just moving a "pointer" from one commit to another. It doesn't even look at the files!

I know, the 2nd diagram may look more complicated, but there are really just two tiny steps: creating a refactoring branch and merging it into the feature branch. It's just two lines:
git checkout -b "refactoring"
git merge refactoring
And you don't have to commit anything after the merge. The key difference here is that the correct way of doing things lets Git keep track of the common ancestor of each commit. It then knows how to merge the refactoring into the feature branch because it can go back and see their common history - which is the secret sauce that makes Git merges so delicious. 

This is nice because it also lets you switch between tasks quite easily. Each has its own branch, after all. And at the end of the day you can pick and choose which branches you want to merge into the "trunk" and which to stow away for another day or toss out the window. And it just works

So I learned my lesson. You never know where a given task is going to take you. You often find yourself fixing stuff "along the way" and then having to keep track of these fixes in case you stop working on the original task. By creating a branch for every meaningful change that stands on its own you allow yourself to experiment with different ways of solving a problem and you never think twice about doing some refactoring even in the middle of a risky experimental feature.


--------------------------------
* Well, merges nearly always work. If you change the exact same line in multiple branches Git isn't going to try to mash them together. It's just going to tell you to figure it out yourself. Which is great - it's what you want it to do. It takes no chances. When it merges something successfully, you know it's 100% safe. This includes moving functions around between files and within files - almost any sort of change you can think of.