Your app broke, and you have no idea why? Even worse, you don't even know what commit is causing it to fail? Don't worry - it happens.
It is important to keep calm and track that first commit that is causing the failure, and git can help you out. Let's see how...
Finding the (first) bad apple
You start with what you know for sure - the last (present) commit is bad: Then you checkout to some (potentially distant) commit in the past where you know things worked (you check it). Let's call it the good commit:
You can be sure that the first bad commit is somewhere between that good commit and the present bad commit:
To trace it, you can sequentially go through all those in-between commits and check your app at every step.
This process will work fine if there are a few commits to check, but what if there are tens or hundreds?
Because git's commit-graph, in this context, is a sorted list, we can speed up the process by using an algorithm called binary search .
Binary search to the rescue!
You start the same way as before - finding some commit in the (distant) past where things were nice and green:
Now, instead of sequentially checking all the in-between commits, you pick the commit in the middle of your list and check your app's behavior at that point:
If everything works you can consider that commit as a good commit:
You can also be sure that all the commits before it are also good so you don't need to check them individually:
If, on the other hand, the commit in the middle was bad (your app was broken):
Then you'd know for sure that all the commits after it are also bad:
Either way, you end up with a new list of commits to check that's half in size (with a new god/bad commit pair).
You repeat this process until you pinpoint the exact commit where things went sour.
Using binary search significantly cuts the time needed to trace the first bad commit, but all this manual checking out to commits still takes time and it's not much fun. Luckily, git has a command called bisect that will do that for you. All you'll need to do is to decide whether a certain commit is good or bad and git will do the rest.
To start the bisecting process you type:
Then you tell git what commit is bad. If you omit the checksum git will assume it's the last commit on your current branch.
After you tell git what is the "good" commit:
it will display how many steps are left and immediately checkout to that first (middle) commit. Now you can check your app's behavior and tell git if the commit is good or bad:
Once you do, git moves (checks-out) to the next commit and you repeat the process. After a couple of steps (depending on how big your commit list was), you'll reach the final step and git will inform you what the first bad commit is:
Manually checking those commits to decide if they are good or bad is sometimes necessary, maybe it's some legacy app and there's not a lot of tests so you need to manually check your app. If you are lucky and can write a script to check it for you then becomes fully automated.
The script needs to be outside of source control (maybe a folder "up") and return exit code 0 if the commit is considered good or 1 if it's considered bad.
You start the bisect as before, by telling git which commit is good and which is bad.
But now you can tell git to run the bisect process and use your script to decide whether some commit is good or bad:
Now you know what commit was the one to introduce the failure (bug), hopefully, you'll be able to figure out the why part of the question.