What's the purpose of git commit tree at all if it doesn't truthfully represent the development process?
What's the purpose of git commit tree at all if it doesn't truthfully represent the development process?
Pre-push commit history is purely for the developer. A push is a publish, which means you are now presenting work for others to view and understand, code review, maintain, and use to debug. There is no more value to people to see every commit for a typo-fix, rollback, and redirection than there would be if our text files kept a complete and honest history of your every backspace. Nobody else wants to watch the rambling movie. BUT that movie can be important to you for a time, and can remain on your local git repo.
If the commit history is getting long in the tooth, it does help to clean it up fairly often, and I have found that cleanup for long-running branches to be much more valuable to my memory than trying to walk or bisect through every minute of my development process.
A quick note about merges: A merge should have something more interesting than "developer merged branch foo onto master"; a merge introduces something important, and old branch names may not exist in the future at all or on the same commits. If one follows this rule of thumb -- make commit messages useful -- then the fact that you were updating your branch with the latest master becomes unimportant. It is actually noise and confusion for anyone having to dig through commit histories, due to the massive number of forks. In other words, always rebase onto master first before merging.
I look at commit history as a document that I can polish and present to others. I look at my code the same way, trying to make it readable for other humans. This is writing prose. I group like things together. Each commit is a solid microfeature. There are no typo commits.
One squashed commit per feature is also terrible. A commit should contain a set of changes that accomplishes one thing, whatever that means to you and your team.
By example: this morning I was working on some huge refactoring and already had 5 short meaningful commits (not all with proper commit message yet) when I noticed some small bug creepd in so I added a line to fix it. That line then goes in it's own fixup commit for one of the earlier commits. Likewise, I noticed one of the new interfaces would be better if it had 2 more methods. So: new fixup commit again. A couple of commits later one logical piece of the work was done, so I did a rebase to get all the fixup commits amended to where they belong. Then I reviewed all commits carefully, rebasing again and squashing some commits because it made more sense to me like that, and adding/editing proper commit messages on the go. I work best like this (mainly for pretty large changes or new features which add a lot of code - for small fixes here and there it's obviously not as involved) and it does produce a perfecly viable story about the development process, useful to other readers, but only because of the rebasing. Otherwise it would be a mess. This to illustrate it's not always as simple and general as 'please don't do that rebasing madness'.
(i.e. what if git log just showed the highest level aggregate commit message by default)
commenting your changes properly doesn't have to involve destroying history - thats just an accidental failure of git that has caused a lot of people a lot of trouble
(Especially in the way that a lot of old SVN/CVS users came into git wanting a simple straight line view and finding tools like rebase instead of `git log --first-parent`, we've ended up in a bit of a mess where people expect to do the heavy lifting in "sandblaster" tools like rebase instead of better UI/UX tools.)
The argument for rebase is that some of your local commits (especially ones like "going home for the day" or "whoops typo") are just as useless a level of detail as your other random keystrokes.
My feature development branch will have a commit for each logical change, typically 3-5/day done by staging individual chunks with "git add -p" (exclusively) as a "I'm done with this piece" checkpoint, still leaving all debug and still in-progress parts in my working copy only. But I wouldn't hesitate to do a "I'm moving from my laptop to my desktop" work-in-progress commit either. Later commits will frequently undo changes done in earlier feature commits, or just revert a commit outright if it proved to go down the wrong development path. You really can't commit too often in my opinion, and it's incredibly freeing knowing you can make sweeping changes and then back out of them at a moments notice.
For merging, I will rebase on master, squash everything into 1 or a few logical commits, and _review_ the resulting commit diff manually as if I were to send it to a maintainer. I will write an extensive commit message based on the code review as a kindness to my future self. The development branch still exists and can be referred to at any time, but I don't want to see that cruft when looking at the project log.
This leads to a commit style where each commit has a well-defined purpose rather than just being a snapshot of the code at a point in time. You make frequent commits while writing the code, but once it's working, you go back and create distinct commits for each distinct change. While you're splitting your changes up, you sometimes realize you forgot something, made a mistake, or left some junk around you didn't mean to commit, which is a nice secondary benefit.
There are two kinds of commits: permanent history and work-in-progress current stack of commits. The permanent history should not be edited except in extreme circumstance. The WIP stack should be edited mercilessly until it looks as good as possible.
In git, the separation between history and WIP is usually enforced by social conventions (e.g. a decree to the effect of "never rebase master, only rebase features branches"). Mercurial codifies this social convention into the repository by marking commits as public or as draft depending if they've been shared on a publishing repo or not, and forbidding editing public commits without a heavy use of `--force`.