Here's an odd thing about the git bisect
command: It has only 1 option
(--no-checkout). Compare with eg git commit
, which has 36 options by my
count.
The difference is largely down to git having a pervasive culture of
carefully edited history. We need lots of git commit
options to carefully
produce commits that look Just Right. Staging only some of the files we've
edited, perhaps even staging only some of the changes within a file. Amend
that commit if we notice we made a mistake. Create a whole series of beautiful
commits, and use rebase later to remix them into a more beautiful whole.
Beautiful fake histories. Because coding is actually messy; our actual edit history contains blind alleys and doublings back on itself; contains periods of many days the code isn't building properly. We want to sweep that complexity away, hide it under the rug. This works well except when it doesn't, when some detail airbrushed out of the only remaining history turns out to be important.
Once we have these beautiful fake histories of changes, we can easily bisect them and find the commit that introduced a bug. So bisect doesn't need a lot of options to control how it works.
I'd like to suggest a new option though. At least as a thought experiment. --merges-only would make bisect only check the merge commits in the range of commits being bisected. The bisection would result in not a single commit, but in the set of commits between two merges.
I suspect this would be useful for faster bisecting some histories of the beautiful fake kind. But I know it would be useful when the history is messy and organic and full of false starts and points where the code doesn't build. Merges, in such histories, are often the points where things reach a certian level of beauty, where that messy feature branch got to the point it all built again (please let this happen today) and was merged into master. Bisecting such points in a messy organic history should work about as well as bisecting carefully gardened histories.
I think I'll save the full rant about beautiful fake history vs messy real history for some other day. Or maybe I've already ranted that rant here before, I can't remember.
Let's just say that I personally come down on the side of liking my git history to reflect the actual code I was working on, even if it was broken and even if I threw it away later. I've indeed taken this to extreme lengths with propellor; in its git history you can see every time I've ever run it, and the version of my config file and code at that point. Apologies to anyone who's been put off by that... But oddly, propellor gets by far more contributions from others than any of my other haskell programs.
All in the form of beaufiully constructed commits, naturally.
Mercurial's revsets already easily allow this.
First you mark your good and your bad revision as usual...
hg bisect --bad -r . # Mark current revision as bad
hg bisect --good -r .~100 # Mark the 100th ancestor as good
hg bisect --skip -r "bisect('untested') and not merge()' # Skip all untested bisection commits that are not merges
There are lots of good ideas in Mercurial. I don't think you are one to shy away from unpopular but interesting software. Give Mercurial a try!
Both of the alternatives (the real messy history and the beautiful fake history) have their own barriers to entry to committing. The real messy history is of course sometimes a bit harder to follow, but on the other hand it shows that software is not written by people who just wake up one morning, sit down and write perfect bugless code in one sitting. It's easier to feel one is 'good enough' to contribute when the contribution is going into a project that clearly has allowed others to learn from mistakes before.
While I agree with the general sentiment of this blog post (GitHub's homepage now advertises that its git GUI allows you to "craft the perfect commit"), I don't like propellor's automatically committing to git every time it is run. The problem there is that it doesn't give the user an opportunity to write a commit message, which makes it much more fiddly to perform reversions. If I can look at my branch history and know what each (recent) commit does just by looking at the commit message, it's easy to see what I need to revert to undo something that I've decided was a bad idea. If changes are hidden in "propellor spin" commits, I end up having to check the diffs of each of those commits to be sure I've reverted everything I wanted to revert.
I've worked around this by wrapping
propellor --spin
in a simple shell script that bails if I've any uncommitted changes, so I can ignore all "propellor spin" commits since all they do is make sure the head of the branch is PGP-signed.That's why I threw this together; https://github.com/lakeman/git-snapshot
The best of both worlds. Add it to your make process to preserve the contents of your working folder, while leaving your HEAD commit alone so you can keep trying to write a more beautiful history.
Never used it myself, and it definitely needs more work to be useful in the real world.