mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 00:14:41 +01:00
Make gitstatus prompt field values None
when there is no git repo (#4920)
Make gitstatus prompt fields None when there is no git repo
This commit is contained in:
parent
6fa83f8305
commit
588881803c
3 changed files with 85 additions and 48 deletions
23
news/fix-gitstatus-no-git.rst
Normal file
23
news/fix-gitstatus-no-git.rst
Normal file
|
@ -0,0 +1,23 @@
|
|||
**Added:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Changed:**
|
||||
|
||||
* When there is no git repository, the values of all ``gitstatus`` prompt fields are now ``None``.
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Removed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* When there is no git repository, ``$PROMPT`` format strings like ``{gitstatus: hello {}}`` now work as expected.
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
|
@ -79,19 +79,14 @@ def test_gitstatus_clean(prompts, fake_proc):
|
|||
def test_no_git(prompts, fake_process, tmp_path):
|
||||
os.chdir(tmp_path)
|
||||
err = b"fatal: not a git repository (or any of the parent directories): .git"
|
||||
for cmd in (
|
||||
"git status --porcelain --branch",
|
||||
"git rev-parse --git-dir",
|
||||
"git diff --numstat",
|
||||
):
|
||||
fake_process.register_subprocess(
|
||||
command=cmd,
|
||||
stderr=err,
|
||||
returncode=128,
|
||||
)
|
||||
fake_process.register_subprocess(
|
||||
command="git rev-parse --git-dir", stderr=err, returncode=128
|
||||
)
|
||||
|
||||
exp = ""
|
||||
assert prompts.pick_val("gitstatus.repo_path") == ""
|
||||
assert format(prompts.pick("gitstatus")) == exp
|
||||
assert _format_value(prompts.pick("gitstatus"), None, None) == exp
|
||||
assert _format_value(prompts.pick("gitstatus"), "{}", None) == exp
|
||||
# test that all gitstatus fields (gitstatus, gitstatus.branch,
|
||||
# gitstatus.porceclain, etc) are None and are formatted correctly in a
|
||||
# format string like {gitstatus: hello {}}
|
||||
for field in prompts.get_fields(gitstatus):
|
||||
assert prompts.pick_val(field) is None
|
||||
assert _format_value(prompts.pick(field), None, None) == ""
|
||||
assert _format_value(prompts.pick(field), "hello {}", None) == ""
|
||||
|
|
|
@ -92,12 +92,48 @@ def _get_sp_output(xsh, *args: str, **kwargs) -> str:
|
|||
return out
|
||||
|
||||
|
||||
class _GSField(PromptField):
|
||||
class _GitDir(PromptField):
|
||||
_cwd = ""
|
||||
|
||||
def update(self, ctx):
|
||||
# call the subprocess only if cwd changed
|
||||
# or if value is None (in case `git init` was run)
|
||||
from xonsh.dirstack import _get_cwd
|
||||
|
||||
cwd = _get_cwd()
|
||||
if cwd != self._cwd or self.value is None:
|
||||
self._cwd = cwd
|
||||
self.value = _get_sp_output(
|
||||
ctx.xsh, "git", "rev-parse", "--git-dir"
|
||||
).strip()
|
||||
if self.value == "":
|
||||
self.value = None
|
||||
|
||||
|
||||
repo_path = _GitDir()
|
||||
|
||||
|
||||
def inside_repo(ctx):
|
||||
return ctx.pick_val(repo_path) is not None
|
||||
|
||||
|
||||
class GitStatusPromptField(PromptField):
|
||||
"""Only calls the updator if we are inside a git repository"""
|
||||
|
||||
def update(self, ctx):
|
||||
if inside_repo(ctx):
|
||||
if self.updator:
|
||||
self.updator(self, ctx)
|
||||
else:
|
||||
self.value = None
|
||||
|
||||
|
||||
class _GSField(GitStatusPromptField):
|
||||
"""wrap output from git command to value"""
|
||||
|
||||
_args: "tuple[str, ...]" = ()
|
||||
|
||||
def update(self, ctx):
|
||||
def updator(self, ctx):
|
||||
self.value = _get_sp_output(ctx.xsh, *self._args).strip()
|
||||
|
||||
|
||||
|
@ -105,7 +141,7 @@ short_head = _GSField(prefix=":", _args=("git", "rev-parse", "--short", "HEAD"))
|
|||
tag = _GSField(_args=("git", "describe", "--always"))
|
||||
|
||||
|
||||
@PromptField.wrap()
|
||||
@GitStatusPromptField.wrap()
|
||||
def tag_or_hash(fld: PromptField, ctx):
|
||||
fld.value = ctx.pick(tag) or ctx.pick(short_head)
|
||||
|
||||
|
@ -116,23 +152,6 @@ def _parse_int(val: str, default=0):
|
|||
return default
|
||||
|
||||
|
||||
class _GitDir(_GSField):
|
||||
_args = ("git", "rev-parse", "--git-dir")
|
||||
_cwd = ""
|
||||
|
||||
def update(self, ctx):
|
||||
# call the subprocess only if cwd changed
|
||||
from xonsh.dirstack import _get_cwd
|
||||
|
||||
cwd = _get_cwd()
|
||||
if cwd != self._cwd:
|
||||
self._cwd = cwd
|
||||
super().update(ctx)
|
||||
|
||||
|
||||
repo_path = _GitDir()
|
||||
|
||||
|
||||
def get_stash_count(gitdir: str):
|
||||
"""Get git-stash count"""
|
||||
with contextlib.suppress(OSError):
|
||||
|
@ -141,7 +160,7 @@ def get_stash_count(gitdir: str):
|
|||
return 0
|
||||
|
||||
|
||||
@PromptField.wrap(prefix="⚑")
|
||||
@GitStatusPromptField.wrap(prefix="⚑")
|
||||
def stash_count(fld: PromptField, ctx: PromptFields):
|
||||
fld.value = get_stash_count(ctx.pick_val(repo_path))
|
||||
|
||||
|
@ -160,7 +179,7 @@ def get_operations(gitdir: str):
|
|||
yield name
|
||||
|
||||
|
||||
@PromptField.wrap(prefix="{CYAN}", separator="|")
|
||||
@GitStatusPromptField.wrap(prefix="{CYAN}", separator="|")
|
||||
def operations(fld, ctx: PromptFields) -> None:
|
||||
gitdir = ctx.pick_val(repo_path)
|
||||
op = fld.separator.join(get_operations(gitdir))
|
||||
|
@ -170,7 +189,7 @@ def operations(fld, ctx: PromptFields) -> None:
|
|||
fld.value = ""
|
||||
|
||||
|
||||
@PromptField.wrap()
|
||||
@GitStatusPromptField.wrap()
|
||||
def porcelain(fld, ctx: PromptFields):
|
||||
"""Return parsed values from ``git status --porcelain``"""
|
||||
|
||||
|
@ -228,7 +247,7 @@ def get_gitstatus_info(fld: "_GSInfo", ctx: PromptFields) -> None:
|
|||
fld.value = info[fld.info]
|
||||
|
||||
|
||||
class _GSInfo(PromptField):
|
||||
class _GSInfo(GitStatusPromptField):
|
||||
info: str
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
@ -246,7 +265,7 @@ conflicts = _GSInfo(prefix="{RED}×", suffix="{RESET}", info="conflicts")
|
|||
staged = _GSInfo(prefix="{RED}●", suffix="{RESET}", info="staged")
|
||||
|
||||
|
||||
@PromptField.wrap()
|
||||
@GitStatusPromptField.wrap()
|
||||
def numstat(fld, ctx):
|
||||
changed = _get_sp_output(ctx.xsh, "git", "diff", "--numstat")
|
||||
|
||||
|
@ -262,17 +281,17 @@ def numstat(fld, ctx):
|
|||
fld.value = (insert, delete)
|
||||
|
||||
|
||||
@PromptField.wrap(prefix="{BLUE}+", suffix="{RESET}")
|
||||
@GitStatusPromptField.wrap(prefix="{BLUE}+", suffix="{RESET}")
|
||||
def lines_added(fld: PromptField, ctx: PromptFields):
|
||||
fld.value = ctx.pick_val(numstat)[0]
|
||||
|
||||
|
||||
@PromptField.wrap(prefix="{RED}-", suffix="{RESET}")
|
||||
@GitStatusPromptField.wrap(prefix="{RED}-", suffix="{RESET}")
|
||||
def lines_removed(fld: PromptField, ctx):
|
||||
fld.value = ctx.pick_val(numstat)[-1]
|
||||
|
||||
|
||||
@PromptField.wrap(prefix="{BOLD_GREEN}", suffix="{RESET}", symbol="✓")
|
||||
@GitStatusPromptField.wrap(prefix="{BOLD_GREEN}", suffix="{RESET}", symbol="✓")
|
||||
def clean(fld, ctx):
|
||||
changes = sum(
|
||||
ctx.pick_val(f)
|
||||
|
@ -319,11 +338,11 @@ class GitStatus(MultiPromptField):
|
|||
continue
|
||||
yield frag
|
||||
|
||||
def _collect(self, ctx):
|
||||
if not ctx.pick_val(repo_path):
|
||||
# no need to display any other fragments
|
||||
return
|
||||
yield from super()._collect(ctx)
|
||||
def update(self, ctx):
|
||||
if inside_repo(ctx):
|
||||
super().update(ctx)
|
||||
else:
|
||||
self.value = None
|
||||
|
||||
|
||||
gitstatus = GitStatus()
|
||||
|
|
Loading…
Add table
Reference in a new issue