Git Gud: How to Avoid/Fix Detached HEAD and Hunt Buggy Commits
Tags: git, learning, development, devops
Published: 2023-04-24 04:01:54

While Git is a very useful version control tool, it can also be harsh and unforgiving to those inexperienced with using it. All of us who've used Git have surely felt the frustrating sting of merge conflicts, branch mismatches, and the dreaded detached HEAD state. This guide will provide some rules of thumb for branch navigation, detached HEAD troubleshooting advice, and some bonus tips and tricks I've picked up in my studies.

**<u>Branch Management</u>**
---

One of the primary ways you can avoid a detached HEAD state is by effective usage of branches to organize your commit history and keep your working tree clean. So, if you're ever about to start making significant changes that might break things in your code, don't be afraid to use a branch!

`git checkout -b <branch name>` will both create a branch named <whatever you put for 'branch name'> and check it out at the same time. Efficient!

Once you've finished whatever work you were doing on your branch, and have tested it to ensure it's working, simply use `git checkout <main branch name>` (typically 'master' or 'main')  to move to the main/master branch, then use `git merge <branch name>` to merge that branch into it.

**<u>Fixing Detached HEAD</u>**
---

To fix this problem, let us briefly delve into what the problem actually is.

The HEAD is essentially just a reference pointing to the most recent commit in a branch. It can be moved, however, with the `git checkout` command, which will allow you to access a version of the code at that commit. Whenever the HEAD is pointing to a specific commit rather than the most recent commit of the currently checked out branch, it is said to be 'detached'. This can cause all sorts of annoying issues, but thankfully, there is an easy fix!

The easiest and most straightforward way to correct this issue is to simply use `git checkout <commit id of the most recent commit on the currently checked out branch>`. If you're not sure which commit this is, you can use `git status`, which should show the HEAD among other commits. 

Of course, most of the time when you check out another commit it's because you want to use some or all of that commit's code. Simply checking out the HEAD won't accomplish that, but thankfully there's an easy solution to that too!

If you want to merge changes from the commit you'd checked out which caused the detached HEAD state, first you just need to create a new branch using the command we'd previously discussed: `git checkout -b <branch name>`. That will copy all of the changes in the currently checked out commit into a new branch. After that, just check out the current HEAD using the command described above. Once you've done that, run `git merge <name of the branch you created in the first step>` and presto, the merge will be initiated! Once you've resolved any merge conflicts, you'll have ended the detached HEAD state and be ready to continue coding!

**<u>Using `git bisect` To Find A Problem Commit</u>**
---

Sometimes, a bug goes unnoticed for a long time and the commit that introduced it is unknown. In a situation like that, we might want to return to an earlier commit, but how can we find which one to return to? Enter `git bisect`!

`git bisect` is a useful command that allows you to choose a starting commit (where you know the code was functional and the bug not present) and an ending commit (where you know the bug is present - typically the current HEAD) and test the commits in between to locate the one that introduced a bug. Here's how to use it:

1. Run `git bisect start`. Nothing will appear to happen after running this command; that's expected.
2. Run `git bisect bad <commit id of latest commit you know is bad>` to start the process. Note that if you omit the commit id, it will use the commit you are currently on as a default.
3. Run `git bisect good <commit id of latest commit you know is working>` to give a starting point to the tool. The tool will then checkout a commit somewhere in between of the 'good' and 'bad' commits you indicated.
4. Test the code at the commit that was checked out, then run either `git bisect good` if the bug isn't present, or `git bisect bad` if it is.
5. After marking each commit the tool checks out as either good or bad, the tool will either checkout a new commit, or end; keep marking commits good or bad until it ends.
6. When the tool ends, it will identify the earliest commit where the bug is present, which will allow you to search through the code there (particularly the changes specific to that commit) and determine what corrections will be needed to fix it
7. Remember, at this point you will be in a detached HEAD state! In order to fix this, run `git bisect reset` to end the bisect session.

**<u>Conclusion</u>**
---

I hope that this has helped you to get better at avoiding the detached HEAD state, empowered you to be able to fix that state should it occur, and given you some information on how to hunt out buggy commits more easily without encountering unintuitive Git errors. Happy coding!


**Sources**:

https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging

https://www.atlassian.com/git/tutorials/using-branches/git-merge

https://git-scm.com/docs/git-bisect

https://git-school.github.io/visualizing-git/

https://www.youtube.com/watch?v=U83Utlp3vYg

https://www.youtube.com/watch?v=HvDjbAa9ZsY

https://www.youtube.com/watch?v=wIY824wWpu4

https://www.youtube.com/watch?v=ecK3EnyGD8o

https://www.youtube.com/watch?v=P3ZR_s3NFvM