Let's say I released a version of my software about a year ago and tagged it at 2.3 in Git.
So I keep adding features and fixing bugs and before you know it, the software is now at version 3.0. But now I have a bug on version 2.3 of the software and the person that needs it fixed is not ready to upgrade to version 3.0.
As far as Git is concerned what would be that best way to manage applying a patch to 2.3 and creating version 2.3.1 of the software without changing the history of the Git repo.
For instance, I can't checkout version 2.3, apply the patch and then tag it at 2.3.1 and push it up since that would create a new head.
How do developers typically manage supporting older versions of their software?
Checking out version 2.3, applying the patch, tagging it 2.3.1 is exactly what you are going to do.
Creating a new head (rather, a new branch) is not a problem whatsoever, it's what git was made for. Note that "HEAD" has no structural meaning in git, it only stands out because it is the one commit that is active in your current working directory. Git only cares about commits, structurally, and you can have as many "top-level" commits as you like.
git checkout 2.3 # gives you a "detached HEAD" git checkout -b dev_2.3 # a new branch, more for convenience ...modify files... git add ... ; git commit ... git tag 2.3.1 git branch -D dev_2.3 # get rid of it if you have a feeling that you won't be comming back soon
Keep in mind that both branches and tags are nothing special at all in git. They are simply sticky notes pointing to commits. Making a (maybe temporary) branch in this manner is just a bit more convenient since you can easily switch to/from it if the backport you are doing involves a bit more than one quick commit.