So, I have this big project, which is in the process of being refactored by me. I am changing a lot of stuff, so there is no chance to get it to compile some time soon. I am living in a special git branch which I named cleanup (which is going to get merged into master eventually, of course).

The problem is, that I/we have the policy to never commit non-compiling code (ideally it should also work, but it must compile and link, at the very least). So, until I am finished with this huge task, I am unable to commit anything (for review or for bookkeeping).
This is not the way I like to work (I believe most people commit at least once a day or so).

What do you think? Is there a solution I am overlooking?
Can I later tell git to aggregate commits or something? I could live with non-compiling commit as long as they stay in the cleanup branch.

Edit

To the subject of pushing/committing: I am aware that it is a huge difference, but later, there will be broken revisions, when I merge my stuff into master. So if you browse through the history (or git bisect ...) then the "local" revisions will be world accessible. So only committing locally and not pushing is not the best solution, because it will cause you trouble later on (when the subject is closed and forgotten for some time).

In short: Local commits will be pushed eventually. The global history should not show non-compiling commits.

You can collect multiple local commits in a single global commit.
– user1249Aug 29 '11 at 14:32

@Thorbjørn is that Jim's recommendation below, or a different mechanism within Git?
– BrianAug 29 '11 at 15:42

I believe so - I cannot remember the exact command.
– user1249Aug 29 '11 at 15:44

5

You aren't refactoring, you're doing something bigger. After each refactor on a codebase, your code should compile and have the exact same observable behavior. You might want to change your question title to reflect that, as my immediate reaction was "Commit what you've got whenever, it all should work".
– David ThornleyAug 29 '11 at 16:05

4 Answers
4

The git merge --squash command allows you to create a single commit on top of the current branch whose effect is the same as merging another branch. The command updates the working tree and stages the changes in the index, so all you have to do next is commit:

a disciplined technique for restructuring an existing body of
code, altering its internal structure without changing its external
behavior.

Its heart is a series of small behavior preserving transformations.
Each transformation (called a “refactoring”) does little, but a
sequence of transformations can produce a significant restructuring.
Since each refactoring is small, it’s less likely to go wrong. The
system is kept fully working after each small refactoring, reducing
the chances that a system can get seriously broken during the
restructuring.

If you apply this method you can commit (and push) on a regular basis.

You might argue that this is impractical, and that it doesn't work for something large scale. This is where the Mikado method can help. You decompose a large refactoring into a series of small refactorings by creating a dependency graph. The method is recursive, try to make your change, it doesn't break anything check it in, otherwise revert your change and make a note of the pre-requisites. One by one, you fix those pre-requisite refactorings until you can achieve your main refactoring goal.

Git can really help this method. You can keep your local (broken) branch. As you commit (and push) the sub-goals you can rebase your main goal branch to on top of the commits you've just made, until it is no longer broken.

Check out the manual page for git rebase, particularly the git rebase -i variant. It allows you to re-order, delete or squash together any number of commits in your history, which sounds like what you are looking for. I use it all the time in exactly the situation you describe: making many small commits which are not suitable for public consumption, then squashing them together into a single "refactoring" commit before pushing to the shared repository.

You are using Git, thus committing does not necessarily imply pushing your changes....

IMHO, and working with Git, it is perfectly good to commit your work, even if it does not compile... because, after all, once you commit your changes, no one will have the code available (until you push it). Of course, before pushing it, you must ensure it works well and compile, so that others can fetch and merge your changes without any problems.

Also, you are working on a different branch than the master one. So, if you want (and I recommend this), you won't push your branch ever. Once you have finished the refactoring, just checkout master branch, merge your changes and push master branch.

I'd -1 if I could; the commits will become available! Local work-in-progress commits are great, but never push them; it makes code harder to understand, interrupts git bisect, and complicates the history. Rebase or squash instead.
– RJFalconerMay 14 '12 at 15:29