1

I'd like my commandline (PS1 variable) to display detached heads more elegantly, even if it's a little more ambiguous. The reason I want to do this is because while I never work on detached branches, I frequently encounter them due to our project setup, and want to know what branch they were from (or at least, a branch that they are on).

First of all, I believe I have a sound understanding of the difference between:

  • The head commit of master
  • A detached head, which happens to be the same as the head of master

That's the difference between these two commands:

git checkout master   # Checkout the master branch
git checkout master~0 # Checkout the commit from the head of the master branch

Now onto the problem. At the moment, my PS1 contains $(__git_ps1) at the end, and commandline shows this as a result:

# git checkout master~1
addison:~/project ((111abcdef1...))$ 

# git checkout master~0
addison:~/project ((000abcdef0...))$ 

# git checkout master
addison:~/project (master)$ 

What I want to happen, is if I'm on a detached head, to be able to find a branch which has a matching commit hash (prefer master), and display the branchname, and how far behind HEAD the commit is, like this:

# git checkout master~1
addison:~/project (Detached(master~1))$ 

# git checkout master~0
addison:~/project (Detached(master))$ 

# git checkout master
addison:~/project (master)$ 

I understand that there may not be a utility that does this already, and I'm ready to accept that - If that is the case, I'd like to know how I can go about finding a branchname for a commit hash, and how far behind the branch's HEAD commit it is. I know that there may be multiple branches that have a commit with the same hash - I just want a 'best effort' solution. That might mean just choosing the most recent, or the closest to HEAD, etc.

Using this information, I can make my own script and embed it in my $PS1 variable, and format it exactly how I want.


UPDATE

I found out there is an option that can be set with $(__git_ps1), which can change the format of the output to be exactly how I want:

GIT_PS1_DESCRIBE_STYLE=contains # git describe --contains HEAD
GIT_PS1_DESCRIBE_STYLE=branch   # git describe --contains --all HEAD
GIT_PS1_DESCRIBE_STYLE=tag      # git describe --tags HEAD
GIT_PS1_DESCRIBE_STYLE=describe # git describe HEAD
GIT_PS1_DESCRIBE_STYLE=default  # git describe --tags --exact-match HEAD

If I set the option to branch, then the output is much more readable.

Addison
  • 7,322
  • 2
  • 39
  • 55
  • It's difficult to solve this problem in general, but `git status` has done a pretty good job of solving it in specific: `git status` will say *detached at * or *detached from * if possible, and *detached at * if not, in modern Git. To do this it looks at the `HEAD` reflog to find the most recent branch checkout before the detaching. Another option to consider is `git describe --contains`, which might be more along the lines of what you're looking for; consider adding `--all` to that. – torek Sep 06 '19 at 02:17
  • My `git status` only says: `HEAD detached at 000abcdef0` - it says nothing about the fact that it is related to the `master` branch in any way. But that `git describe --contains --all`! That's EXACTLY what I was looking for - I can absolutely work that into a script. Feel free to put that down as an answer. – Addison Sep 06 '19 at 02:34

1 Answers1

1

As you've noted, there isn't a perfect solution. There are two approaches that Git itself uses. One is exemplified by git status: when you're on some branch, HEAD contains the branch name, and when you're detached, HEAD contains a hash ID, but the reflog for HEAD still has the branch name in it, and Git can scan that and pick out a recent branch and see if you're on that commit or one of its descendants and say detached at name or detached from name.1

In your case, though, you might want something more like what git describe does, only in the order that's not the default for git describe. In this case, what you want is more like what git describe --contains does. The --contains option implies the --tags option, which has git describe look at all tags, but not look at any branch names. Fortunately, you can add --all:

git describe --contains --all

which looks at tag and branch names—and other references too, including refs/stash, which may not be so great—and picks one of those to describe the current commit. So this may be the closest thing Git has built in to what you want.


1This feature was not in some very old versions of Git. I'm not sure when it first appeared, and the release notes only mention it in describing a fix to Git 2.4.0. Before 2.4.0, git branch and git status disagreed in when and how they said "detached at" vs "detached from". So it's in 2.4 and later, but earlier versions are less good at it, and at some point, the "detached at/from" stuff just isn't there at all.

torek
  • 448,244
  • 59
  • 642
  • 775