Imagine we are both working on a project. I don't care to know that you merged 3 times from master yesterday before pushing your feature. Also I don't care to know details like you forgot to put a config file in your first commit and had to do a second one, or that it took you 3 commits to have the spelling alright in the UI.
Mainly that information is useful to you. It is also mostly only usable efficiently by you ( I will read your whole feature, most likely I will not be able to rollback to the middle of your change )
For example I commit several time an hour - my coworkers would be pissed if I make 10 commits for each minor feature I develop.
I'm incredibly new to Git, but actually destroying the history seems like a crude solution for a sophisticated tool like Git.
Couldn't there be some way to just tag the "main" commits and mark the dead ends as "extraneous" rather than destroying them? And then have your history-viewing tool hide/squash the unmarked "invisible" commits by default and only expose them when specifically requested?
I mean, it seems to make more sense to just look at a blind alley of commits you made and just flag them all as a mistake rather than actually rearranging the DAG.
This! I have been thinking for a while that this whole weird business about rebasing and 'clean' history is really a response to a shortcoming in our tools, which don't give us a way to distinguish from 'small' incremental, work-in-progress, historical-record-but-not-that-interesting commits, and 'big' significant, feature-completing commits.
In Mercurial, you can kindasorta have this if you do all your development on named branches, and only merge the named branches back into the default branch at these significant moments. Then, you can merge willy-nilly, without rebasing or otherwise destroying or lying about history, and distinguish ignorable work-in-progress merges from significant feature-complete merges by which branch they were on. Most query commands let you filter by branch, so you can easily do that.
For those not familiar with Mercurial, the difference that allows this is that Mercurial permanently records the name of the branch a commit was added to. That means there is an observable difference between merging A into B and merging B into A. This is not universally agreed to be a good feature, but it does allow this particular approach.
Then you just have to choose between having a single shared development branch, a branch per developer, a branch per story, a branch per task, etc, and come up with a coping strategy for any resulting proliferation of branches.
> Couldn't there be some way to just tag the "main" commits and mark the dead ends as "extraneous" rather than destroying them? And then have your history-viewing tool hide/squash the unmarked "invisible" commits by default and only expose them when specifically requested?
You could do exactly that; look into git-update-ref for how you could implement that so that garbage collection doesn't wipe out those dead ends (git-notes basically does what you would need to do).
Note that you would still be rewriting history, still rearranging the DAG, but you would have references to the old states. Basically like a permanent reflog, though perhaps with an interface tailored to this usage.
Just wanted to point out that rebasing is not just to remove or squash commits. I use it to:
- _separate_ large commits into atomic, logical units
- fixup changes missed the first time around
- rewrite commit messages to ensure they're clear
- reorder commits
It could combine all consecutive non-merge commits; or, if you're rigidly adhering to the "never directly commit" model for master, collapsing everything down to just the merge commits would be enough.
I silence the noise before merging on a public branch by squashing the "thinking commits" on my private branch. What remains is a clean history of commits. You will not find the oscillating commits on the public branch but you will find them on my private (local) branch.
So you do commit early and often, just that nobody else sees your commits until the feature is complete and working. And then only after you rearranged and squashed your commits. From the outside it looks like you always commit top quality code from the first try.
Besides the official repository, we have per-user backed-up repositories for this reason. People develop on their machines and publish/save on the git server finished/unfinished work. You can rebase at will and push --force as much as you want.
Rebases on the official repo are not allowed and the per-user repos are public and can be used also for collaboration.
Do they also force you to commit unfinished work frequently? Because in my experience devs would get around that "all branches are public" rule by just not putting things into a commit until they were confident something was complete. The end result is the same, except that devs don't get to enjoy using version control to its full extent.
Unfinished work is committed somewhat often, especially if it's complex. Commits are sometimes squashed once a pull request is approved (PRs being the code review gateway), more often if there are a lot of uninteresting commits.
Some people use git add -i to be very selective about what they commit, and deliberately increase the number of independent commits, rather than have a single commit that has unrelated changes in it.
Other people have a lot of WIP commits.
We started out doing more rebasing, but do a lot less now, after having run into issues with having e.g. branched off of a branch that has since been rebased and merged (e.g. front end / back end feature split). You try to rebase, but the replay of commits continuously hits merge conflicts, and you spend about 30 minutes repeatedly fixing the same conflicts. And then there's the risk of your force push accidentally chopping off someone else's commit (though that's never happened).
We had one PITA case where a profanity was checked into the codebase, and branches with the offending commit in the history lurked in various places surprisingly long after the commit had been excised from the main trunk lines. Since we're a startup in the financial sector, our code will be in escrow situations, potentially examined by humorless auditors, we don't want profanities in.
How do you enforce that with Git? Any time you create a branch, it's local until you push it.
But anyhow, just give people private repos on the server. What I do is push my private WIP branches to my home directory on the server, and once it's ready for code review and merge, push it to the central repository.