Eu queria colocar o hash git atual no meu prompt; mas minha abordagem inicial foi um pouco lenta:
psvar[1]="$(git log -n1 --pretty='format: %h' 2>/dev/null)"
(com $PROMPT
inclusão %1v
para tornar isso visível)
Eu tentei o vcs_info
plugin, mas ele também estava lento. Então acabei escrevendo minha própria função:
function githead() {
local commondir ref hash _
local gitdir="${1:a}"
until [[ -e "$gitdir/.git" ]]; do
local updir="${gitdir%/*}"
[[ "$updir" == "$gitdir" ]] && return 1
gitdir="$updir"
done
gitdir="$gitdir/.git"
[[ -f "$gitdir" ]] && read _ gitdir < "$gitdir"
read _ ref < "$gitdir/HEAD"
local commondir="$gitdir/commondir"
[[ -e "$commondir" ]] && gitdir="$gitdir/$(<$commondir)"
ref="$gitdir/$ref"
read hash <"$ref"
print -r "${hash::12}"
return 0
}
Certamente é mais rápido, mas isso é obrigado a ser frágil. Em particular, alguns dos arquivos que tenho que ler parecem que podem conter mais de uma linha de conteúdo, e não necessariamente colocar a linha que eu quero no topo.
Existe uma maneira rápida de obter o hash atual do git, que seja mais robusta do que o que escrevi manualmente?
A ferramenta de baixo nível para expandir refs é
git rev-parse
:Refs normais apontam apenas para um único commit por vez. Há pouquíssimas exceções, como FETCH_HEAD, mas essas não podem ser alvo de HEAD de qualquer forma.
O problema, no entanto, não são as múltiplas linhas, mas o fato de que as refs podem ser compactadas – não há garantia de que elas existam como arquivos discretos em
.git/refs/
; elas podem ser apenas uma linha dentro.git/packed-refs
(ou até mesmo algum outro "banco de dados de ref" recém-introduzido nas últimas versões do 'git').A implementação bash que eu uso no meu próprio prompt – nunca achei a indicação "commit atual" útil, então ela mostra exatamente para onde HEAD aponta e não tenta desreferenciá-lo de forma alguma. (Se ela me mostra um ID de commit bruto, então eu sei que estou em um HEAD desanexado.)
Levando em consideração os pontos levantados por @grawity, continuei com minha implementação manual, mas corrigi alguns erros e adicionei um fallback:
Aqui eu deliberadamente verifico o conteúdo de
HEAD
para decidir se ele tem umref:
prefixo ou se é um hash bruto (o head case destacado). Eu também uso o conjunto de resultados de erro por$(<file)
quando o arquivo não é legível, em vez de executar um teste separado de antemão (consequentemente encapsulei tudo em{} 2>/dev/null
).Embora eu tenha escrito e testado uma função que lia o
packed-refs
arquivo, decidi que isso não fazia sentido porque, se está lá, provavelmente é porque é grande e minha implementação provavelmente seria mais lenta do que apenas chamar o git. Então, chamei o git.E a última linha adiciona um espaço precedente se o hash não estiver vazio, para que eu não acabe com espaços em ambos os lados de uma string vazia.