git-prompt: stop manually parsing HEAD with unknown ref formats

We're manually parsing the HEAD reference in git-prompt to figure out whether it is a symbolic or direct reference. This makes it intimately tied to the on-disk format we use to store references and will stop working once we gain additional reference backends in the Git project. Ideally, we would refactor the code to exclusively use plumbing tools to read refs such that we do not have to care about the on-disk format at all. Unfortunately though, spawning processes can be quite expensive on some systems like Windows. As the Git prompt logic may be executed quite frequently we try very hard to spawn as few processes as possible. This refactoring is thus out of question for now. Instead, condition the logic on the repository's ref format: if the repo uses the the "files" backend we can continue to use the old logic and read the respective files from disk directly. If it's anything else, then we use git-symbolic-ref(1) to read the value of HEAD. This change makes the Git prompt compatible with the upcoming "reftable" format. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jan 4, 2024 at 09:21 UTC fc134b41ce2ee7b2a98a988db6eeb109e11a2831
1 file changed +24 -7
contrib/completion/git-prompt.sh
+24 -7
@@ -408,7 +408,7 @@ __git_ps1 ()
408
409 local repo_info rev_parse_exit_code
410 repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
411 - --is-bare-repository --is-inside-work-tree \
411 + --is-bare-repository --is-inside-work-tree --show-ref-format \
412 --short HEAD 2>/dev/null)"
413 rev_parse_exit_code="$?"
414
@@ -421,6 +421,8 @@ __git_ps1 ()
421 short_sha="${repo_info##*$'\n'}"
422 repo_info="${repo_info%$'\n'*}"
423 fi
424 + local ref_format="${repo_info##*$'\n'}"
425 + repo_info="${repo_info%$'\n'*}"
426 local inside_worktree="${repo_info##*$'\n'}"
427 repo_info="${repo_info%$'\n'*}"
428 local bare_repo="${repo_info##*$'\n'}"
@@ -479,12 +481,25 @@ __git_ps1 ()
481 b="$(git symbolic-ref HEAD 2>/dev/null)"
482 else
483 local head=""
482 - if ! __git_eread "$g/HEAD" head; then
483 - return $exit
484 - fi
485 - # is it a symbolic ref?
486 - b="${head#ref: }"
487 - if [ "$head" = "$b" ]; then
484 +
485 + case "$ref_format" in
486 + files)
487 + if ! __git_eread "$g/HEAD" head; then
488 + return $exit
489 + fi
490 +
491 + if [[ $head == "ref: "* ]]; then
492 + head="${head#ref: }"
493 + else
494 + head=""
495 + fi
496 + ;;
497 + *)
498 + head="$(git symbolic-ref HEAD 2>/dev/null)"
499 + ;;
500 + esac
501 +
502 + if test -z "$head"; then
503 detached=yes
504 b="$(
505 case "${GIT_PS1_DESCRIBE_STYLE-}" in
@@ -502,6 +517,8 @@ __git_ps1 ()
517
518 b="$short_sha..."
519 b="($b)"
520 + else
521 + b="$head"
522 fi
523 fi
524 fi