mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
Add support for Fossil SCM branch names in vc.py. (#5046)
Add support for Fossil VCS in vc.py. The prompt now shows the currently active Fossil branch, while inside a Fossil checkout.
This commit is contained in:
parent
237e231e7f
commit
cb95f0e487
3 changed files with 108 additions and 20 deletions
24
news/vc.rst
Normal file
24
news/vc.rst
Normal file
|
@ -0,0 +1,24 @@
|
|||
**Added:**
|
||||
|
||||
* Display the current branch of Fossil VCS checkouts in the prompt,
|
||||
similar to git and hg.
|
||||
|
||||
**Changed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Removed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
|
@ -1,5 +1,7 @@
|
|||
import os
|
||||
import subprocess as sp
|
||||
import textwrap
|
||||
from typing import Dict, List
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock
|
||||
|
||||
|
@ -8,7 +10,19 @@ import pytest
|
|||
from xonsh.prompt import vc
|
||||
|
||||
# Xonsh interaction with version control systems.
|
||||
VC_BRANCH = {"git": {"master", "main"}, "hg": {"default"}}
|
||||
VC_BRANCH = {
|
||||
"git": {"master", "main"},
|
||||
"hg": {"default"},
|
||||
"fossil": {"trunk"},
|
||||
}
|
||||
VC_INIT: Dict[str, List[List[str]]] = {
|
||||
# A sequence of commands required to initialize a repository
|
||||
"git": [["init"]],
|
||||
"hg": [["init"]],
|
||||
# Fossil "init" creates a central repository file with the given name,
|
||||
# "open" creates the working directory at another, arbitrary location.
|
||||
"fossil": [["init", "test.fossil"], ["open", "test.fossil"]]
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(params=VC_BRANCH.keys())
|
||||
|
@ -20,28 +34,32 @@ def repo(request, tmpdir_factory):
|
|||
temp_dir = Path(tmpdir_factory.mktemp("dir"))
|
||||
os.chdir(temp_dir)
|
||||
try:
|
||||
sp.call([vc, "init"])
|
||||
for init_command in VC_INIT[vc]:
|
||||
sp.call([vc] + init_command)
|
||||
except FileNotFoundError:
|
||||
pytest.skip(f"cannot find {vc} executable")
|
||||
if vc == "git":
|
||||
git_config = temp_dir / ".git/config"
|
||||
git_config.write_text(
|
||||
"""
|
||||
[user]
|
||||
name = me
|
||||
email = my@email.address
|
||||
[init]
|
||||
defaultBranch = main
|
||||
"""
|
||||
)
|
||||
|
||||
# git needs at least one commit
|
||||
Path("test-file").touch()
|
||||
sp.call(["git", "add", "test-file"])
|
||||
sp.call(["git", "commit", "-m", "test commit"])
|
||||
_init_git_repository(temp_dir)
|
||||
return {"vc": vc, "dir": temp_dir}
|
||||
|
||||
|
||||
def _init_git_repository(temp_dir):
|
||||
git_config = temp_dir / ".git/config"
|
||||
git_config.write_text(textwrap.dedent(
|
||||
"""\
|
||||
[user]
|
||||
name = me
|
||||
email = my@email.address
|
||||
[init]
|
||||
defaultBranch = main
|
||||
"""
|
||||
))
|
||||
# git needs at least one commit
|
||||
Path("test-file").touch()
|
||||
sp.call(["git", "add", "test-file"])
|
||||
sp.call(["git", "commit", "-m", "test commit"])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def set_xenv(xession, monkeypatch):
|
||||
def _wrapper(path):
|
||||
|
@ -52,8 +70,15 @@ def set_xenv(xession, monkeypatch):
|
|||
|
||||
|
||||
def test_test_repo(repo):
|
||||
test_vc_dir = repo["dir"] / ".{}".format(repo["vc"])
|
||||
assert test_vc_dir.is_dir()
|
||||
if repo["vc"] == "fossil":
|
||||
# Fossil stores the check-out meta-data in a special file within the open check-out.
|
||||
# At least one of these below must exist
|
||||
metadata_file_names = {".fslckout", "_FOSSIL_"} # .fslckout on Unix, _FOSSIL_ on Windows
|
||||
existing_files = set(file.name for file in repo["dir"].iterdir())
|
||||
assert existing_files.intersection(metadata_file_names)
|
||||
else:
|
||||
test_vc_dir = repo["dir"] / ".{}".format(repo["vc"])
|
||||
assert test_vc_dir.is_dir()
|
||||
if repo["vc"] == "git":
|
||||
test_file = repo["dir"] / "test-file"
|
||||
assert test_file.exists()
|
||||
|
|
|
@ -124,6 +124,27 @@ def get_hg_branch(root=None):
|
|||
return branch
|
||||
|
||||
|
||||
def _run_fossil_cmd(cmd):
|
||||
timeout = XSH.env.get("VC_BRANCH_TIMEOUT")
|
||||
result = subprocess.check_output(cmd, stderr=subprocess.DEVNULL, timeout=timeout)
|
||||
return result
|
||||
|
||||
|
||||
def get_fossil_branch():
|
||||
"""Attempts to find the current fossil branch. If this could not
|
||||
be determined (timeout, not in a fossil checkout, etc.) then this returns None.
|
||||
"""
|
||||
# from fossil branch --help: "fossil branch current: Print the name of the branch for the current check-out"
|
||||
cmd = "fossil branch current".split()
|
||||
try:
|
||||
branch = xt.decode_bytes(_run_fossil_cmd(cmd))
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
branch = None
|
||||
else:
|
||||
branch = RE_REMOVE_ANSI.sub("", branch.splitlines()[0])
|
||||
return branch
|
||||
|
||||
|
||||
_FIRST_BRANCH_TIMEOUT = True
|
||||
|
||||
|
||||
|
@ -156,7 +177,7 @@ def _vc_has(binary):
|
|||
|
||||
def current_branch():
|
||||
"""Gets the branch for a current working directory. Returns an empty string
|
||||
if the cwd is not a repository. This currently only works for git and hg
|
||||
if the cwd is not a repository. This currently only works for git, hg, and fossil
|
||||
and should be extended in the future. If a timeout occurred, the string
|
||||
'<branch-timeout>' is returned.
|
||||
"""
|
||||
|
@ -165,6 +186,8 @@ def current_branch():
|
|||
branch = get_git_branch()
|
||||
if not branch and _vc_has("hg"):
|
||||
branch = get_hg_branch()
|
||||
if not branch and _vc_has("fossil"):
|
||||
branch = get_fossil_branch()
|
||||
if isinstance(branch, subprocess.TimeoutExpired):
|
||||
branch = "<branch-timeout>"
|
||||
_first_branch_timeout_message()
|
||||
|
@ -234,6 +257,20 @@ def hg_dirty_working_directory():
|
|||
return None
|
||||
|
||||
|
||||
def fossil_dirty_working_directory():
|
||||
"""Returns whether the fossil checkout is dirty. If this could not
|
||||
be determined (timeout, file not found, etc.) then this returns None.
|
||||
"""
|
||||
cmd = ["fossil", "changes"]
|
||||
try:
|
||||
status = _run_fossil_cmd(cmd)
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
status = None
|
||||
else:
|
||||
status = bool(status)
|
||||
return status
|
||||
|
||||
|
||||
def dirty_working_directory():
|
||||
"""Returns a boolean as to whether there are uncommitted files in version
|
||||
control repository we are inside. If this cannot be determined, returns
|
||||
|
@ -244,6 +281,8 @@ def dirty_working_directory():
|
|||
dwd = git_dirty_working_directory()
|
||||
if dwd is None and _vc_has("hg"):
|
||||
dwd = hg_dirty_working_directory()
|
||||
if dwd is None and _vc_has("fossil"):
|
||||
dwd = fossil_dirty_working_directory()
|
||||
return dwd
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue