Commit Graph

109 Commits (v2.50.0-rc2)

Author SHA1 Message Date
Avi Halachmi (:avih) fbcdfab348 git-prompt: support custom 0-width PS1 markers
When using colors, the shell needs to identify 0-width substrings
in PS1 - such as color escape sequences - when calculating the
on-screen width of the prompt.

Until now, we used the form %F{<color>} in zsh - which it knows is
0-width, or otherwise use standard SGR esc sequences wrapped between
byte values 1 and 2 (SOH, STX) as 0-width start/end markers, which
bash/readline identify as such.

But now that more shells are supported, the standard SGR sequences
typically work, but the SOH/STX markers might not be identified.

This commit adds support for vars GIT_PS1_COLOR_{PRE,POST} which
set custom 0-width markers or disable the markers.

Signed-off-by: Avi Halachmi (:avih) <avihpit@yahoo.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-20 08:28:19 -07:00
Avi Halachmi (:avih) 0dbe3d3f16 git-prompt: ta-da! document usage in other shells
With one big exception, git-prompt.sh should now be both almost posix
compliant, and also compatible with most (posix-ish) shells.

That exception is the use of "local" vars in functions, which happens
extensively in the current code, and is not simple to replace with
posix compliant code (but also not impossible).

Luckily, almost all shells support "local" as used by the current
code, with the notable exception of ksh93[u+m], but also the Schily
minimal posix sh (pbosh), and yash in posix mode.

See assessment below that "local" is likely the only blocker in those.

So except mainly ksh93, git-prompt.sh now works in most shells:
- bash, zsh, dash since at least 0.5.8, free/net bsd sh, busybox-ash,
  mksh, openbsd sh, pdksh(!), Schily extended Bourne sh (bosh), yash.

which is quite nice.

As an anecdote, replacing the 1st line in __git_ps1() (local exit=$?)
with these 2 makes it work in all tested shells, even without "local":

  # handles only 0/1 args for simplicity. needs +5 LOC for any $#
  __git_e=$?; local exit="$__git_e" 2>/dev/null ||
    {(eval 'local() { export "$@"; }'; __git_ps1 "$@"); return "$__git_e"; }

Explanation:

  If the shell doesn't have the command "local", define our own
  function "local" which instead does plain (global) assignents.
  Then use __git_ps1 in a subshell to not clober the caller's vars.

  This happens to work because currently there are no name conflicts
  (shadow) at the code, initial value is not assumed (i.e. always
  doing either 'local x=...'  or 'local x;...  x=...'), and assigned
  initial values are quoted (local x="$y"), preventing word split and
  glob expansion (i.e. assignment context is not assumed).

  The last two (always init, quote values) seem to be enough to use
  "local" portably if supported, and otherwise shells indeed differ.

  Uses "eval", else shells with "local" may reject it during parsing.
  We don't need "export", but it's smaller than writing our own loop.

While cute, this approach is not really sustainable because all the
vars become global, which is hard to maintain without conflicts
(but hey, it currently has no conflicts - without even trying...).

However, regardless of being an anecdote, it provides some support to
the assessment that "local" is the only blocker in those shells.

Signed-off-by: Avi Halachmi (:avih) <avihpit@yahoo.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-20 08:28:19 -07:00
Avi Halachmi (:avih) 29bcec82a6 git-prompt: don't use shell $'...'
$'...' is new in POSIX (2024), and some shells support it in recent
versions, while others have had it for decades (bash, zsh, ksh93).

However, there are still enough shells which don't support it, and
it's cheap to use an alternative form which works in all shells,
so let's do that instead of dismissing it as "it's compliant".

It was agreed to use one form rather than $'...' where supported and
fallback otherwise.

shells where $'...' works:
- bash, zsh, ksh93, mksh, busybox-ash, dash master, free/net bsd sh.

shells where it doesn't work, but the new fallback works:
- all dash releases (up to 0.5.12), older versions of free/net bsd sh,
  openbsd sh, pdksh, all Schily Bourne sh variants, yash.

Signed-off-by: Avi Halachmi (:avih) <avihpit@yahoo.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-20 08:28:18 -07:00
Avi Halachmi (:avih) b732e08671 git-prompt: add some missing quotes
The issues which this commit fixes are unlikely to be broken
in real life, but the fixes improve correctness, and would prevent
bugs in some uncommon cases, such as weird IFS values.

Listing some portability guidelines here for future reference.

I'm leaving it to someone else to decide whether to include
it in the file itself, place it as a new file, or not.

---------

The command "local" is non standard, but is allowed in this file:
- Quote initialization if it can expand (local x="$y"). See below.
- Don't assume initial value after "local x". Either initialize it
  (local x=..), or set before first use (local x;.. x=..; <use $x>).
  (between shells, "local x" can unset x, or inherit it, or do x= )

Other non-standard features beyond "local" are to be avoided.

Use the standard "test" - [...] instead of non-standard [[...]] .

--------

Quotes (some portability things, but mainly general correctness):

Quotes prevent tilde-expansion of some unquoted literal tildes (~).
If the expansion is undesirable, quotes would ensure that.
  Tilds expanded: a=~user:~/ ;  echo ~user ~/dir
  not expanded:   t="~"; a=${t}user  b=\~foo~;  echo "~user" $t/dir

But the main reason for quoting is to prevent IFS field splitting
(which also coalesces IFS chars) and glob expansion in parts which
contain parameter/arithmetic expansion or command substitution.

"Simple command" (POSIX term) is assignment[s] and/or command [args].
Examples:
  foo=bar         # one assignment
  foo=$bar x=y    # two assignments
  foo bar         # command, no assignments
  x=123 foo bar   # one assignment and a command

The assignments part is not IFS-split or glob-expanded.

The command+args part does get IFS field split and glob expanded,
but only at unquoted expanded/substituted parts.

In the command+args part, expanded/substituted values must be quoted.
(the commands here are "[" and "local"):
  Good: [ "$mode" = yes ]; local s="*" x="$y" e="$?" z="$(cmd ...)"
  Bad:  [ $mode = yes ];   local s=*   x=$y   e=$?   z=$(cmd...)

The arguments to "local" do look like assignments, but they're not
the assignment part of a simple command; they're at the command part.

Still at the command part, no need to quote non-expandable values:
  Good:                 local x=   y=yes;   echo OK
  OK, but not required: local x="" y="yes"; echo "OK"
But completely empty (NULL) arguments must be quoted:
  foo ""   is not the same as:   foo

Assignments in simple commands - with or without an actual command,
don't need quoting becase there's no IFS split or glob expansion:
  Good:   s=* a=$b c=$(cmd...)${x# foo }${y-   } [cmd ...]
  It's also OK to use double quotes, but not required.

This behavior (no IFS/glob) is called "assignment context", and
"local" does not behave with assignment context in some shells,
hence we require quotes when using "local" - for compatibility.

The value between 'case' and 'in' doesn't IFS-split/glob-expand:
  Good:       case  * $foo $(cmd...)  in ... ; esac
  identical:  case "* $foo $(cmd...)" in ... ; esac

Nested quotes in command substitution are fine, often necessary:
  Good: echo "$(foo... "$x" "$(bar ...)")"

Nested quotes in substring ops are legal, and sometimes needed
to prevent interpretation as a pattern, but not the most readable:
  Legal:  foo "${x#*"$y" }"

Nested quotes in "maybe other value" subst are invalid, unnecessary:
  Good:  local x="${y- }";   foo "${z:+ $a }"
  Bad:   local x="${y-" "}"; foo "${z:+" $a "}"
Outer/inner quotes in "maybe other value" have different use cases:
  "${x-$y}"  always one quoted arg: "$x" if x is set, else "$y".
  ${x+"$x"}  one quoted arg "$x" if x is set, else no arg at all.
  Unquoted $x is similar to the second case, but it would get split
  into few arguments if it includes any of the IFS chars.

Assignments don't need the outer quotes, and the braces delimit the
value, so nested quotes can be avoided, for readability:
  a=$(foo "$x")  a=${x#*"$y" }  c=${y- };  bar "$a" "$b" "$c"

Signed-off-by: Avi Halachmi (:avih) <avihpit@yahoo.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-20 08:28:18 -07:00
Avi Halachmi (:avih) fe445a1026 git-prompt: replace [[...]] with standard code
The existing [[...]] tests were either already valid as standard [...]
tests, or only required minimal retouch:

Notes:

- [[...]] doesn't do field splitting and glob expansion, so $var
  or $(cmd...) don't need quoting, but [... does need quotes.

- [[ X == Y ]] when Y is a string is same as [ X = Y ], but if Y is
  a pattern, then we need:  case X in Y)... ; esac  .

- [[ ... && ... ]] was replaced with [ ... ] && [ ... ] .

- [[ -o <zsh-option> ]] requires [[...]], so put it in "eval" and only
  eval it in zsh, so other shells would not abort on syntax error
  (posix says [[ has unspecified results, shells allowed to reject it)

- ((x++)) was changed into x=$((x+1))  (yeah, not [[...]] ...)

Shells which accepted the previous forms:
- bash, zsh, ksh93, mksh, openbsd sh, pdksh.

Shells which didn't, and now can process it:
- dash, free/net bsd sh, busybox-ash, Schily Bourne sh, yash.

Signed-off-by: Avi Halachmi (:avih) <avihpit@yahoo.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-20 08:28:18 -07:00
Avi Halachmi (:avih) f2e264e43f git-prompt: don't use shell arrays
Arrays only existed in the svn-upstream code, used to:
- Keep a list of svn remotes.
- Convert commit msg to array of words, extract the 2nd-to-last word.

Except bash/zsh, nearly all shells failed load on syntax errors here.

Now:
- The svn remotes are a list of newline-terminated values.
- The 2nd-to-last word is extracted using standard shell substrings.
- All shells can digest the svn-upstream code.

While using shell field splitting to extract the word is simple, and
doesn't even need non-standard code, e.g. set -- $(git log -1 ...),
it would have the same issues as the old array code: it depends on IFS
which we don't control, and it's subject to glob-expansion, e.g. if
the message happens to include * or **/* (as this commit message just
did), then the array could get huge. This was not great.

Now it uses standard shell substrings, and we know the exact delimiter
to expect, because it's the match from our grep just one line earlier.

The new word extraction code also fixes svn-upstream in zsh, because
previously it used arr[len-2], but because in zsh, unlike bash, array
subscripts are 1-based, it incorrectly extracted the 3rd-to-last word.
symptom: missing upstream status in a git-svn repo: u=, u+N-M, etc.

The breakage in zsh is surprising, because it was last touched by
  commit d0583da838 (prompt: fix show upstream with svn and zsh),
claiming to fix exactly that. However, it only mentions syntax fixes.
It's unclear if behavior was fixed too. But it was broken, now fixed.

Note LF=$'\n' and then using $LF instead of $'\n' few times.
A future commit will add fallback for shells without $'...', so this
would be the only line to touch instead of replacing every $'\n' .

Shells which could run the previous array code:
- bash

Shells which have arrays but were broken anyway:
- zsh: 1-based subscript
- ksh93: no "local" (the new code can't fix this part...)
- mksh, openbsd sh, pdksh: failed load on syntax error: "for ((...))".

More shells which Failed to load due to syntax error:
- dash, free/net bsd sh, busybox-ash, Schily Bourne shell, yash.

Signed-off-by: Avi Halachmi (:avih) <avihpit@yahoo.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-20 08:28:18 -07:00
Avi Halachmi (:avih) 6df4b09159 git-prompt: fix uninitialized variable
First use is in the form:  local var; ...; var=$var$whatever...

If the variable was unset (as bash and others do after "local x"),
then it would error if set -u is in effect.

Also, many shells inherit the existing value after "local var"
without init, but in this case it's unlikely to have a prior value.

Now we initialize it.

(local var= is enough, but local var="" is the custom in this file)

Signed-off-by: Avi Halachmi (:avih) <avihpit@yahoo.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-20 08:28:17 -07:00
Avi Halachmi (:avih) f037e607a8 git-prompt: use here-doc instead of here-string
Here-documend is standard, and works in all shells.

Both here-string and here-doc add final newline, which is important
in this case, because $output is without final newline, but we do
want "read" to succeed on the last line as well.

Shells which support here-string:
- bash, zsh, mksh, ksh93, yash (non-posix-mode).

shells which don't, and got fixed:
- ash-derivatives (dash, free/net bsd sh, busybox-ash).
- pdksh, openbsd sh.
- All Schily Bourne shell variants.

Signed-off-by: Avi Halachmi (:avih) <avihpit@yahoo.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-08-20 08:28:17 -07:00
Ville Skyttä d7805bc743 completion: protect prompt against unset SHOWUPSTREAM in nounset mode
As it stands, the only call site of `__git_ps1_show_upstream` checks
that the `GIT_PS1_SHOWUPSTREAM` variable is set, so this is effectively
a no-op. However, that might change, and chances of noticing the
unprotected use might not be that high when it does.

Signed-off-by: Ville Skyttä <ville.skytta@iki.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-01 12:38:23 -07:00
Ville Skyttä 758b4e1373 completion: fix prompt with unset SHOWCONFLICTSTATE in nounset mode
`GIT_PS1_SHOWCONFLICTSTATE` is a user variable that might not be set,
causing errors when the shell is in `nounset` mode.

Take into account on access by falling back to an empty string.

Signed-off-by: Ville Skyttä <ville.skytta@iki.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-01 08:31:54 -07:00
Patrick Steinhardt fc134b41ce 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>
2024-01-08 11:21:45 -08:00
Junio C Hamano 4ca12e10e6 Merge branch 'ek/completion-use-read-r-to-read-literally'
The completion script used to use bare "read" without the "-r"
option to read the contents of various state files, which risked
getting confused with backslashes in them.  This has been
corrected.

* ek/completion-use-read-r-to-read-literally:
  completion: suppress unwanted unescaping of `read`
2023-05-02 10:13:34 -07:00
Edwin Kofler 197152098a completion: suppress unwanted unescaping of `read`
The function `__git_eread`, which reads the first line from the file,
calls the `read` builtin without passing the flag option `-r`.  When
the `read` builtin is called without the flag `-r`, it processes the
backslash escaping in the text that it reads.  For this reason, it is
generally considered the best practice to always use the `read`
builtin with flag `-r` unless one intensionally processes the
backslash escaping.  For the present case in git-prompt.sh, in fact,
all the occurrences of the calls of `__git_eread` intend to read the
literal content of the first lines.

To make it read the first line literally, pass the flag `-r` to the
`read` builtin in the function `__git_eread`.

Signed-off-by: Edwin Kofler <edwin@kofler.dev>
Signed-off-by: Koichi Murase <myoga.murase@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-04-20 15:47:38 -07:00
Felipe Contreras 7ee1af8cb8 completion: prompt: use generic colors
When the prompt command mode was introduced in 1bfc51ac81 (Allow
__git_ps1 to be used in PROMPT_COMMAND, 2012-10-10), the assumption was
that it was necessary in order to properly add colors to PS1 in bash,
but this wasn't true.

It's true that the \[ \] markers add the information needed to properly
calculate the width of the prompt, and they have to be added directly to
PS1, a function returning them doesn't work.

But that is because bash coverts the \[ \] markers in PS1 to \001 \002,
which is what readline ultimately needs in order to calculate the width.

We don't need bash to do this conversion, we can use \001 \002
ourselves, and then the prompt command mode is not necessary to display
colors.

This is what functions returning colors are supposed to do [1].

[1] http://mywiki.wooledge.org/BashFAQ/053

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
Tested-by: Joakim Petersen <joak-pet@online.no>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-03-16 15:58:22 -07:00
Justin Donnelly e03acd0d4a git-prompt: show presence of unresolved conflicts at command prompt
If GIT_PS1_SHOWCONFLICTSTATE is set to "yes", show the word "CONFLICT"
on the command prompt when there are unresolved conflicts.

Example prompt: (main|CONFLICT)

Signed-off-by: Justin Donnelly <justinrdonnelly@gmail.com>
Acked-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-08-19 10:58:40 -07:00
Joakim Petersen 0e5d9ef395 git-prompt: fix expansion of branch colour codes
Because of the wrapping of the branch name variable $b, the colour codes
in the variable don't get applied, but are instead printed directly in
the output. Move the wrapping of $b to before colour codes are inserted
to correct this. Revert move of branch name colour codes in tests, as
the branch name is now coloured after the wrapping instead of before.

Signed-off-by: Joakim Petersen <joak-pet@online.no>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-06-10 09:41:49 -07:00
Joakim Petersen 9470605a1b git-prompt: make colourization consistent
The short upstream state indicator inherits the colour of the last short
state indicator before it (if there is one), and the sparsity state
indicator inherits this colour as well. This behaviour was introduced by
0ec7c23cdc (git-prompt: make upstream state indicator location
consistent, 2022-02-27), while before this change the aforementioned
indicators were white/the default text colour. Some examples to
illustrate this behaviour (assuming all indicators are enabled and
colourization is on):
 * If there is something in the stash, both the '$' and the short
   upstream state indicator following it will be blue.
 * If the local tree has new, untracked files and there is nothing in
   the stash, both the '%' and the short upstream state indicator
   will be red.
 * If all local changes are added to the index and the stash is empty,
   both the '+' and the short upstream state indicator following it will
   be green.
 * If the local tree is clean and there is nothing in the stash, the
   short upstream state indicator will be white/${default text colour}.

This appears to be an unintended side-effect of the change, and makes
little sense semantically (e.g. why is it bad to be in sync with
upstream when you have uncommitted local changes?). The cause of the
change in colourization is that previously, the short upstream state
indicator appeared immediately after the rebase/revert/bisect/merge
state indicator (note the position of $p in $gitstring):

	local f="$h$w$i$s$u"
	local gitstring="$c$b${f:+$z$f}${sparse}$r$p"

Said indicator is prepended with the clear colour code, and the short
upstream state indicator is thus also uncoloured. Now, the short
upstream state indicator follows the sequence of colourized indicators,
without any clearing of colour (again note the position of $p, now in
$f):

	local f="$h$w$i$s$u$p"
	local gitstring="$c$b${f:+$z$f}${sparse}$r${upstream}"

If the user is in a sparse checkout, the sparsity state indicator
follows a similar pattern to the short upstream state indicator.
However, clearing colour of the colourized indicators changes how the
sparsity state indicator is colourized, as it currently inherits (and
before the change referenced also inherited) the colour of the last
short state indicator before it. Reading the commit message of the
change that introduced the sparsity state indicator, afda36dbf3
(git-prompt: include sparsity state as well, 2020-06-21), it appears
this colourization also was unintended, so clearing the colour for said
indicator further increases consistency.

Make the colourization of these state indicators consistent by making
all colourized indicators clear their own colour. Make colouring of $c
dependent on it not being empty, as it is no longer being used to colour
the branch name. Move clearing of $b's prefix to before colourization so
it gets cleared properly when colour codes are inserted into it. These
changes make changing the layout of the prompt less prone to unintended
colour changes in the future.

Change coloured Bash prompt tests to reflect the colourization changes:
 * Move the colour codes to wrap the expected content of the expanded
   $__git_ps1_branch_name in all tests.
 * Insert a clear-colour code after the symbol for the first indicator
   in "prompt - bash color pc mode - dirty status indicator - dirty
   index and worktree", to reflect that all indicators should clear
   their own colour.

Signed-off-by: Joakim Petersen <joak-pet@online.no>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-06-07 09:08:39 -07:00
Justin Donnelly 094b5409ea git-prompt: put upstream comments together
Commit 6d158cba28 (bash completion: Support "divergence from upstream"
messages in __git_ps1, 2010-06-17) introduced support for indicating
divergence from upstream in the PS1 prompt. The comments at the top of
git-prompt.sh that were introduced with that commit are several
paragraphs long. Over the years, other comments have been inserted in
between the paragraphs relating to divergence from upstream.

This commit puts the comments relating to divergence from upstream back
together.

Signed-off-by: Justin Donnelly <justinrdonnelly@gmail.com>
Reviewed-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-03-23 13:07:50 -07:00
Justin Donnelly 51d2d67790 git-prompt: make long upstream state indicator consistent
Use a pipe as a separator before long upstream state indicator. This is
consistent with long state indicators for sparse and in-progress
operations (e.g. merge).

For comparison, `__git_ps1` examples without upstream state indicator:
(main)
(main %)
(main *%)
(main|SPARSE)
(main %|SPARSE)
(main *%|SPARSE)
(main|SPARSE|REBASE 1/2)
(main %|SPARSE|REBASE 1/2)

Note that if there are long state indicators, they appear after short
state indicators if there are any, or after the branch name if there are
no short state indicators. Each long state indicator begins with a pipe
(`|`) as a separator.

Before/after examples with long upstream state indicator:
| Before                          | After                           |
| ------------------------------- | ------------------------------- |
| (main u=)                       | (main|u=)                       |
| (main u= origin/main)           | (main|u= origin/main)           |
| (main u+1)                      | (main|u+1)                      |
| (main u+1 origin/main)          | (main|u+1 origin/main)          |
| (main % u=)                     | (main %|u=)                     |
| (main % u= origin/main)         | (main %|u= origin/main)         |
| (main % u+1)                    | (main %|u+1)                    |
| (main % u+1 origin/main)        | (main %|u+1 origin/main)        |
| (main|SPARSE u=)                | (main|SPARSE|u=)                |
| (main|SPARSE u= origin/main)    | (main|SPARSE|u= origin/main)    |
| (main|SPARSE u+1)               | (main|SPARSE|u+1)               |
| (main|SPARSE u+1 origin/main)   | (main|SPARSE|u+1 origin/main)   |
| (main %|SPARSE u=)              | (main %|SPARSE|u=)              |
| (main %|SPARSE u= origin/main)  | (main %|SPARSE|u= origin/main)  |
| (main %|SPARSE u+1)             | (main %|SPARSE|u+1)             |
| (main %|SPARSE u+1 origin/main) | (main %|SPARSE|u+1 origin/main) |

Signed-off-by: Justin Donnelly <justinrdonnelly@gmail.com>
Reviewed-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-03-23 13:07:45 -07:00
Justin Donnelly 0ec7c23cdc git-prompt: make upstream state indicator location consistent
Make upstream state indicator location more consistent with similar
state indicators (e.g. sparse). Group the short upstream state indicator
(`=`, `<`, `>`, or `<>`) with other short state indicators immediately
after the branch name. Previously short and long upstream state
indicators appeared after all other state indicators.

Use a separator (`SP` or `GIT_PS1_STATESEPARATOR`) between branch name
and short upstream state indicator. Previously the short upstream state
indicator would sometimes appear directly adjacent to the branch name
instead of being separated.

For comparison, `__git_ps1` examples without upstream state indicator:
(main)
(main %)
(main *%)
(main|SPARSE)
(main %|SPARSE)
(main *%|SPARSE)
(main|SPARSE|REBASE 1/2)
(main %|SPARSE|REBASE 1/2)

Note that if there are short state indicators, they appear together
after the branch name and separated from it by `SP` or
`GIT_PS1_STATESEPARATOR`.

Before/after examples with short upstream state indicator:
| Before           | After            |
| ---------------- | ---------------- |
| (main=)          | (main =)         |
| (main|SPARSE=)   | (main =|SPARSE)  |
| (main %|SPARSE=) | (main %=|SPARSE) |

Signed-off-by: Justin Donnelly <justinrdonnelly@gmail.com>
Reviewed-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-03-23 13:07:40 -07:00
Justin Donnelly 4d9dc2c57a git-prompt: rename `upstream` to `upstream_type`
In `__git_ps1_show_upstream` rename the variable `upstream` to
`upstream_type`. This allows `__git_ps1_show_upstream` to reference a
variable named `upstream` that is declared `local` in `__git_ps1`, which
calls `__git_ps1_show_upstream`.

Signed-off-by: Justin Donnelly <justinrdonnelly@gmail.com>
Reviewed-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-03-23 13:07:26 -07:00
Elijah Newren 5c0cbdb107 git-prompt: work under set -u
Commit afda36dbf3 ("git-prompt: include sparsity state as well",
2020-06-21) added the use of some variables to control how to show
sparsity state in the git prompt, but implicitly assumed that undefined
variables would be treated as the empty string.  This breaks users who
run under 'set -u'; fix the code to be more explicit.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-05-13 15:50:26 +09:00
Junio C Hamano fb628ab129 Merge branch 'sd/prompt-local-variable'
Code clean-up.

* sd/prompt-local-variable:
  git-prompt.sh: localize `option` in __git_ps1_show_upstream
2020-11-11 13:18:38 -08:00
Sibo Dong 9542d56379 git-prompt.sh: localize `option` in __git_ps1_show_upstream
The variable 'option' is used in __git_ps1_show_upstream()
without being localized.

This clobbers the variable the user may be using for other
purposes, which is bad.  Luckily, $option is not used to carry
information around in the script as a global variable.  The use
of it in this script has very limited scope (namely, only inside
this function), so just declare that it is "local".

Signed-off-by: Sibo Dong <sibo.dong@outlook.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-11-01 16:22:11 -08:00
Felipe Contreras ea625a39fe completion: prompt: fix color for Zsh
We don't need PROMPT_COMMAND in Zsh; we are already using %F{color} %f,
which in turn use %{ and %}, which are the equivalent of Bash's
\[ and \].

We can use as many colors as we want and output directly into PS1
(or RPS1) without the risk of buffer wrapping issues.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-10-28 14:30:59 -07:00
David J. Malan e8882a87d9 git-prompt: change == to = for zsh's sake
When using git-prompt.sh with zsh, __git_ps1 currently errs
when inside a repo with:

__git_ps1:96: = not found

Avoid using non-portable "==" that is only understood by bash
and not zsh. Change to "=" so that the prompt script becomes
usable with zsh again.

Signed-off-by: David J. Malan <malan@harvard.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-20 17:37:20 -07:00
Elijah Newren afda36dbf3 git-prompt: include sparsity state as well
git-prompt includes the current branch, a bunch of single character
mini-state displayers, and some much longer in-progress state
notifications.  The current branch is always shown.  The single
character mini-state displayers are all off by default (they are not
self explanatory) but each has an environment variable for turning it
on.  The in-progress state notifications provide no configuration
options for turning them off, and can be up to 15 characters long (e.g.
"|REBASE (12/18)" or "|CHERRY-PICKING").

The single character mini-state tends to be used for things like "Do you
have any stashes in refs/stash?" or "Are you ahead or behind of
upstream?".  These are things which users can take advantage of but do
not affect most normal git operations.  The in-progress states, by
contrast, suggest the user needs to interact differently and may also
prevent some normal operations from succeeding (e.g. git switch may show
an error instead of switching branches).

Sparsity is like the in-progress states in that it suggests a
fundamental different interaction with the repository (many of the files
from the repository are not present in your working copy!).  A few
commits ago added sparsity information to wt_longstatus_print_state(),
grouping it with other in-progress state displays.  We do similarly here
with the prompt and show the extra state, by default, with an extra
    |SPARSE
This state can be present simultaneously with the in-progress states, in
which case it will appear before the other states; for example,
    (branchname|SPARSE|REBASE 6/10)

The reason for showing the "|SPARSE" substring before other states is to
emphasize those other states.  Sparsity is probably not going to change
much within a repository, while temporary operations will.  So we want
the state changes related to temporary operations to be listed last, to
make them appear closer to where the user types and make them more
likely to be noticed.

The fact that sparsity isn't just cached metadata or additional
information is what leads us to show it more similarly to the
in-progress states, but the fact that sparsity is not transient like the
in-progress states might cause some users to want an abbreviated
notification of sparsity state or perhaps even be able to turn it off.
Allow GIT_PS1_COMPRESSSPARSESTATE to be set to request that it be
shortened to a single character ('?'), and GIT_PS1_OMITSPARSESTATE to be
set to request that sparsity state be omitted from the prompt entirely.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-06-22 09:29:59 -07:00
Elijah Newren 30b00f009c git-prompt: document how in-progress operations affect the prompt
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-06-22 09:29:59 -07:00
Elijah Newren 6d04ce75c4 git-prompt: change the prompt for interactive-based rebases
In the past, we had different prompts for different types of rebases:
   REBASE: for am-based rebases
   REBASE-m: for merge-based rebases
   REBASE-i: for interactive-based rebases

It's not clear why this distinction was necessary or helpful; when the
prompt was added in commit e75201963f ("Improve bash prompt to detect
various states like an unfinished merge", 2007-09-30), it simply added
these three different types.  Perhaps there was a useful purpose back
then, but there have been some changes:

  * The merge backend was deleted after being implemented on top of the
    interactive backend, causing the prompt for merge-based rebases to
    change from REBASE-m to REBASE-i.
  * The interactive backend is used for multiple different types of
    non-interactive rebases, so the "-i" part of the prompt doesn't
    really mean what it used to.
  * Rebase backends have gained more abilities and have a great deal of
    overlap, sometimes making it hard to distinguish them.
  * Behavioral differences between the backends have also been ironed
    out.
  * We want to change the default backend from am to interactive, which
    means people would get "REBASE-i" by default if we didn't change
    the prompt, and only if they specified --am or --whitespace or -C
    would they get the "REBASE" prompt.
  * In the future, we plan to have "--whitespace", "-C", and even "--am"
    run the interactive backend once it can handle everything the
    am-backend can.

For all these reasons, make the prompt for any type of rebase just be
"REBASE".

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-16 15:40:42 -08:00
Phillip Wood e981bf7525 git-prompt: improve cherry-pick/revert detection
If the user commits or resets a conflict resolution in the middle of a
sequence of cherry-picks or reverts then CHERRY_PICK_HEAD/REVERT_HEAD
will be removed and so in the absence of those files we need to check
.git/sequencer/todo to see if there is a cherry-pick or revert in
progress.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-07-01 12:39:44 -07:00
Robert Abel 041fe8fc83 git-prompt: fix reading files with windows line endings
If any of the files read by __git_eread have \r\n line endings, read
will only strip \n, leaving \r. This results in an ugly prompt, where
instead of

    user@pc MINGW64 /path/to/repo (BARE:master)

the last parenthesis is printed over the beginning of the prompt like

    )ser@pc MINGW64 /path/to/repo (BARE:master

This patch fixes the issue by changing the internal field separator
variable IFS to $'\r\n' before using the read builtin command.

Note that ANSI-C Quoting/POSIX Quoting ($'...') is supported by bash
as well as zsh, which are the current targets of git-prompt, cf.
contrib/completion/git-prompt.sh.

Signed-off-by: Robert Abel <rabel@robertabel.eu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-12-06 10:08:13 -08:00
Robert Abel 5501f500b2 git-prompt: make __git_eread intended use explicit
__git_eread is used to read a single line of a given file (if it exists)
into a single variable stripping the EOL.
This patch removes the unused capability to split file contents into tokens
by passing multiple variable names. Add a comment and explicitly use $2
instead of misleading $@ as argument to the read builtin command.

Signed-off-by: Robert Abel <rabel@robertabel.eu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-12-06 10:08:12 -08:00
Michael J Gruber 9f0b4dd0b8 git-prompt: add a describe style for any tags
git-prompt has various describe styles, among them "describe" (by
annotated tags) and "default" (by exact match with any tag).

Add a mode "tag" that describes by any tag, annotated or not.

Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-15 13:19:41 -07:00
Junio C Hamano be099661f4 Merge branch 'vs/prompt-avoid-unset-variable'
The git-prompt scriptlet (in contrib/) was not friendly with those
who uses "set -u", which has been fixed.

* vs/prompt-avoid-unset-variable:
  git-prompt.sh: Don't error on null ${ZSH,BASH}_VERSION, $short_sha
2016-06-27 09:56:47 -07:00
Ville Skyttä 34d8f5a8aa git-prompt.sh: Don't error on null ${ZSH,BASH}_VERSION, $short_sha
When the shell is in "nounset" or "set -u" mode, referencing unset or
null variables results in an error. Protect $ZSH_VERSION and
$BASH_VERSION against that, and initialize $short_sha before use.

Signed-off-by: Ville Skyttä <ville.skytta@iki.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-06 13:09:07 -07:00
SZEDER Gábor c26f70ceb3 bash prompt: indicate dirty index even on orphan branches
__git_ps1() doesn't indicate dirty index while on an orphan branch.

To check the dirtiness of the index, __git_ps1() runs 'git diff-index
--cached ... HEAD', which doesn't work on an orphan branch,
because HEAD doesn't point to a valid commit.

Run 'git diff ... --cached' instead, as it does the right thing both
on valid and invalid HEAD, i.e. compares the index to the existing
HEAD in the former case and to the empty tree in the latter.  This
fixes the two failing tests added in the first commit of this series.

The dirtiness of the worktree is already checked with 'git diff' and
is displayed correctly even on an orphan branch.

Signed-off-by: SZEDER Gábor <szeder@ira.uka.de>
Signed-off-by: Jeff King <peff@peff.net>
2015-11-24 15:27:01 -05:00
SZEDER Gábor 0af9f7ecb8 bash prompt: remove a redundant 'git diff' option
To get the dirty state indicator __git_ps1() runs 'git diff' with
'--quiet --exit-code' options.  '--quiet' already implies
'--exit-code', so the latter is unnecessary and can be removed.

Signed-off-by: SZEDER Gábor <szeder@ira.uka.de>
Signed-off-by: Jeff King <peff@peff.net>
2015-11-24 15:27:01 -05:00
SZEDER Gábor dd160d794f bash prompt: faster untracked status indicator with untracked directories
If the untracked status indicator is enabled, __git_ps1() looks for
untracked files by running 'git ls-files'.  This can be perceptibly slow
in case of an untracked directory containing lot of files, because it
lists all files found in the untracked directory only to be redirected
into /dev/null right away (this is the actual command run by __git_ps1()):

  $ ls untracked-dir/ |wc -l
  100000
  $ time git ls-files --others --exclude-standard --error-unmatch \
    -- ':/*' >/dev/null 2>/dev/null

  real	0m0.955s
  user	0m0.936s
  sys	0m0.016s

Eliminate this delay by additionally passing the '--directory
--no-empty-directory' options to 'git ls-files' to show only the name of
non-empty untracked directories instead of all their content:

  $ time git ls-files --others --exclude-standard --directory \
    --no-empty-directory --error-unmatch -- ':/*' >/dev/null 2>/dev/null

  real	0m0.010s
  user	0m0.008s
  sys	0m0.000s

This follows suit of ea95c7b8f5 (completion: improve untracked directory
filtering for filename completion, 2013-09-18).

Signed-off-by: SZEDER Gábor <szeder@ira.uka.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-20 13:08:56 -07:00
Junio C Hamano 51d5980ea0 Merge branch 'jc/prompt-document-ps1-state-separator' into maint
Docfix.

* jc/prompt-document-ps1-state-separator:
  git-prompt.sh: document GIT_PS1_STATESEPARATOR
2015-07-15 11:41:26 -07:00
Junio C Hamano d70bc3b97a Merge branch 'jc/prompt-document-ps1-state-separator'
Docfix.

* jc/prompt-document-ps1-state-separator:
  git-prompt.sh: document GIT_PS1_STATESEPARATOR
2015-07-01 14:02:32 -07:00
Joe Cridge ab7fade919 git-prompt.sh: document GIT_PS1_STATESEPARATOR
The environment variable GIT_PS1_STATESEPARATOR can be used to set the
separator between the branch name and the state symbols in the prompt.

At present the variable is not mentioned in the inline documentation which
makes it difficult for the casual user to identify.

Signed-off-by: Joe Cridge <joe.cridge@me.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-10 14:53:14 -07:00
Junio C Hamano 5f15cba2f9 Merge branch 'ct/prompt-untracked-fix'
The prompt script (in contrib/) did not show the untracked sign
when working in a subdirectory without any untracked files.

* ct/prompt-untracked-fix:
  git prompt: use toplevel to find untracked files
2015-03-25 12:54:18 -07:00
Cody A Taylor 9bdc5173f0 git prompt: use toplevel to find untracked files
The __git_ps1() prompt function would not show an untracked state
when all the untracked files are outside the current working
directory.

Signed-off-by: Cody A Taylor <codemister99@yahoo.com>
Helped-by: SZEDER Gábor <szeder@ira.uka.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-03-15 14:23:22 -07:00
Junio C Hamano 9920c71825 Merge branch 'tf/prompt-preserve-exit-status'
Using the exit status of the last command in the prompt, e.g.
PS1='$(__git_ps1) $? ', did not work well because the helper
function stomped on the exit status.

* tf/prompt-preserve-exit-status:
  git-prompt: preserve value of $? in all cases
2015-01-14 12:35:49 -08:00
Junio C Hamano e1ef7d177c Merge branch 'rh/hide-prompt-in-ignored-directory'
* rh/hide-prompt-in-ignored-directory:
  git-prompt.sh: allow to hide prompt for ignored pwd
  git-prompt.sh: if pc mode, immediately set PS1 to a plain prompt
2015-01-14 12:34:01 -08:00
Tony Finch 6babe76496 git-prompt: preserve value of $? in all cases
Signed-off-by: Tony Finch <dot@dotat.at>
Reviewed-by: SZEDER Gábor <szeder@ira.uka.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-01-14 10:11:49 -08:00
Junio C Hamano 487b17de3e Merge branch 'tf/prompt-preserve-exit-status'
Using the exit status of the last command in the prompt, e.g.
PS1='$(__git_ps1) $? ', did not work well because the helper
function stomped on the exit status.

* tf/prompt-preserve-exit-status:
  git-prompt: preserve value of $? inside shell prompt
2015-01-07 13:09:35 -08:00
Jess Austin 0120b8c85c git-prompt.sh: allow to hide prompt for ignored pwd
Optionally set __git_ps1 to display nothing when present working
directory is ignored, triggered by the new environment variable
GIT_PS1_HIDE_IF_PWD_IGNORED. This environment variable may be
overridden on any repository by setting bash.hideIfPwdIgnored to
"false". In the absence of GIT_PS1_HIDE_IF_PWD_IGNORED this change
has no effect.

Many people manage e.g. dotfiles in their home directory with git.
This causes the prompt generated by __git_ps1 to refer to that "top
level" repo while working in any descendant directory. That can be
distracting, so this patch helps one shut off that noise.

Signed-off-by: Jess Austin <jess.austin@gmail.com>
Signed-off-by: Richard Hansen <rhansen@bbn.com>
Reviewed-by: Richard Hansen <rhansen@bbn.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-01-07 10:30:30 -08:00
Richard Hansen 76b4309400 git-prompt.sh: if pc mode, immediately set PS1 to a plain prompt
At the beginning of __git_ps1, right after determining that the
function is running in pc mode, set PS1 to a plain (undecorated)
prompt.  This makes it possible to simply return early without having
to set PS1 if the prompt should not be decorated.

Signed-off-by: Richard Hansen <rhansen@bbn.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-01-07 10:27:53 -08:00
Junio C Hamano 35b5a8b769 Merge branch 'jg/prompt-localize-temporary'
"git-prompt" (in contrib/) used a variable from the global scope,
possibly contaminating end-user's namespace.

* jg/prompt-localize-temporary:
  git-prompt.sh: make $f local to __git_eread()
2014-12-22 12:28:20 -08:00