26

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.

7
  • 1
    You can collect multiple local commits in a single global commit.
    – user1249
    Aug 29, 2011 at 14:32
  • @Thorbjørn is that Jim's recommendation below, or a different mechanism within Git?
    – Brian
    Aug 29, 2011 at 15:42
  • I believe so - I cannot remember the exact command.
    – user1249
    Aug 29, 2011 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". Aug 29, 2011 at 16:05
  • 1
    @bitmask How about a rewrite? Mar 9, 2014 at 22:26

4 Answers 4

22

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:

git checkout master
git merge --squash cleanup
git commit -m "Merge cleanup branch"

The git rebase -i command can also squash commits but requires more work.

16

A rewrite is not a refactoring

I realize you're interested in how to use Git, but I would argue that you should consider changing the way you do refactorings more than the way you use Git (although I do think Git can help you).

Martin Fowler defines refactoring as:

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.

5

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.

0
3

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.

Edit

In that case you can use git cherry-pick or play around with git rebase

3
  • An important note, but I considered this. Please see my edit. Thank you.
    – bitmask
    Aug 29, 2011 at 14:13
  • See my edit above.
    – Cristian
    Aug 29, 2011 at 14:27
  • 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.
    – RJFalconer
    May 14, 2012 at 15:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.