To avoid unnecessarily detached HEADs when checking out a certain git SHA with a (python) script, I'd like to check out a branch instead, if that SHA happens to be the current HEAD of a branch.
Ideally, I want to feed git an SHA, and it returns a branch name if the SHA is on a branch's current tip, or error out if not.
git describe --all --exact-match <SHA>
very nearly is what I need, only that it is primarily aimed at tags, and so if a branch and a tag point at my SHA (which often happens in our release branches, for example), only the tag is given. This is not useful, because checking out the tag leads to a detached HEAD (even if a branch points at the same SHA).
Note, I don't want to do git branch --contains
- I don't need to know which branches contain my commit.
If there's no command like git describe
, just for branches, I know I can cross-check my SHA against branch SHAs via git show-ref
. That's not the most elegant solution, though.
I could also do git name-rev --name-only <hash>
, but I'd have to manually check the output for ~
characters, which feels unelegant if there's a git command somewhere to do the same thing.
You could try something like this:
git for-each-ref --format="%(refname:short) %(objectname)" 'refs/heads/' |
grep SHA1 | cut -d " " -f 1
That should give you a list of branches that are currently at the revision SHA1
.
If you are going to use python, this is what I would do:
import subprocess
def get_name(target):
p = subprocess.Popen(['git', 'for-each-ref', 'refs/heads/'], stdout=subprocess.PIPE)
for line in p.stdout:
sha1, kind, name = line.split()
if sha1 != target:
continue
return name
return None
Another option is to use the power of eval to construct a dictionary:
d = '{' + subprocess.check_output(['git', 'for-each-ref', '--python', '--format=%(objectname): %(refname),', 'refs/heads/']) + '}'
name = eval(d)[sha1]
With the upcoming git 2.7 (Q4 2015) you will get a more complete version of git for-each-ref
, which now support the --points-at
git for-each-ref --points-at <branch_name>
With the doc:
--points-at <object>:
Only list refs which points at the given object.
That will allow to check if a commit is part of that list or not.
See commit 4a71109 , commit ee2bd06 , commit f266c91 , commit 9d306b5 , commit 7c32834 , commit 35257aa , commit 5afcb90 , ..., commit b2172fd (07 Jul 2015), and commit af83baf (09 Jul 2015) by Karthik Nayak ( KarthikNayak
) .
(Merged by Junio C Hamano -- gitster
-- in commit 9958dd8 , 05 Oct 2015)
Some features from "
git tag -l
" and "git branch -l
" have been made available to "git for-each-ref
" so that eventually the unified implementation can be shared across all three, in a follow-up series or two.
* kn/for-each-tag-branch:
for-each-ref: add '--contains' option
ref-filter: implement '--contains' option
parse-options.h: add macros for '--contains' option
parse-option: rename parse_opt_with_commit()
for-each-ref: add '--merged' and '--no-merged' options
ref-filter: implement '--merged' and '--no-merged' options
ref-filter: add parse_opt_merge_filter()
for-each-ref: add '--points-at' option
ref-filter: implement '--points-at' option
For my case, I wanted to know if HEAD pointed to a branch post-checkout and then exit 0 if pointing to a branch, or exit non-zero if not pointing to a branch, which is most likely detached HEAD state assuming the checkout succeeded. git symbolic-ref --short HEAD
does this and also returns the name of the branch when pointing to one. Obviously, there's better solutions for what the OP needs, but I wanted to throw this in just in case anyone wanted to make use of the information.
To get the branch names referenced by a commit or tag, I came up with this .gitconfig
:
[alias]
branch-name = !"git for-each-ref --format=\"%(refname:short) %(objectname)\" 'refs/heads/' | sed -En 's/^(\\S+)\\s+'\"$(git rev-parse \"$1\" 2>/dev/null)\"'$/\\1/p' | grep . || { echo \"$1: not a branch\" >&2; false;}
Eg:
$ git branch-head-name da8ecd90
master
Edge cases:
Note: the alias without .gitconfig
string quoting is:
branch-name=!git for-each-ref --format="%(refname:short) %(objectname)" 'refs/heads/' | sed -En 's/^(\S+)\s+'"$(git rev-parse "$1" 2>/dev/null)"'$/\1/p' | grep . || { echo "$1: not a branch" >&2; false;}
我认为解决方案之一,也许不是很优雅,但相当可靠:是列出所有分支branch --contains $sha
然后检查git rev-parse $branch
== $sha
。
What about grep :
grep -R [sha] .git/refs/heads/
Or since you are calling it from a python script, why not just create a function that searches through those files, and returns the branch name or exception as you wanted.
try something like this to test each branch tip hash against your given $sha:
git branch -a| cut -d " " -f 2,3 | xargs -n 1 git rev-parse | grep $sha
or just local
git branch | xargs -n 1 git rev-parse | grep $sha
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.