Situation: Some commit (on master, but not necessarily head of master) has broken things, but it's a big commit and it's not clear what part broke things.
% git checkout master
% git checkout -b bisect-branch
% git revert <offending commit>
(test here to make sure reverting fixed your problem)
% git bisect start
% git bisect good
% while [ "`git diff master`" != "" ]; do echo -e "y\nq\n" | git checkout -p master; git commit -m 'partial undo'; done
(test here to make sure we've recreated your problem)
% git bisect bad
Now just follow the bisect.
The while
line has broken your commit up into small chunks, one commit per chunk. If a chunk has actually broken syntax (so you can't test either way), you can git bisect skip
.
You can also s/master/any-reference-branch-or-tag-or-sha/g
. And you can use s\ny\nq\n
if you want even smaller chunks (but more risk of syntax errors).
This is a great idea, but when I tried it in practice, I didn't get that great results.
You end up with a commit history in file name alphabetical order of changed files, and then linear order of changes within each file. Depending on the exact nature of the changes, sometimes that works very well, other times you will end up with some change early in that history dependent on some change late in it, with the result that almost every commit fails to compile and/or run.
Sometimes a better approach is to try reverting a changed file at a time; if reverting a particular file causes a syntax error/etc, just skip reverting it and then try reverting the next one. Once you've tried reverting them all, you can go back and try again from the start, since sometimes reverting a later file makes it possible to revert an earlier changed file which it depends on. With luck, you'll reduce it down to the single file which causes the breakage. If the changes in that file are complex, then the approach you propose here can be helpful in narrowing down which of those changes is the issue.