Skip to content

Commit 0e81b88

Browse files
committed
Handle common global git options
1 parent f269a60 commit 0e81b88

3 files changed

Lines changed: 176 additions & 13 deletions

File tree

‎pythongit/cli.py‎

Lines changed: 148 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5560,24 +5560,160 @@ def _cmd_repo(argv):
55605560
_register_phase8()
55615561

55625562

5563+
_GLOBAL_FLAGS_IGNORED = {
5564+
"-p", "--paginate", "-P", "--no-pager",
5565+
"--no-replace-objects", "--no-lazy-fetch", "--no-optional-locks",
5566+
"--no-advice", "--literal-pathspecs", "--glob-pathspecs",
5567+
"--noglob-pathspecs", "--icase-pathspecs",
5568+
}
5569+
_GLOBAL_EXIT = "__pygit_global_exit__"
5570+
5571+
5572+
def _remember_env(old_env: dict[str, Optional[str]], key: str) -> None:
5573+
if key not in old_env:
5574+
old_env[key] = os.environ.get(key)
5575+
5576+
5577+
def _set_temp_env(old_env: dict[str, Optional[str]], key: str, value: str) -> None:
5578+
_remember_env(old_env, key)
5579+
os.environ[key] = value
5580+
5581+
5582+
def _restore_env(old_env: dict[str, Optional[str]]) -> None:
5583+
for key, value in old_env.items():
5584+
if value is None:
5585+
os.environ.pop(key, None)
5586+
else:
5587+
os.environ[key] = value
5588+
5589+
5590+
def _normalize_global_path(path: str) -> str:
5591+
return str(Path(path).resolve())
5592+
5593+
5594+
def _consume_global_options(argv: list[str]) -> tuple[list[str], dict[str, Optional[str]], str]:
5595+
old_env: dict[str, Optional[str]] = {}
5596+
old_cwd = os.getcwd()
5597+
out = list(argv)
5598+
i = 0
5599+
while i < len(out):
5600+
a = out[i]
5601+
if a == "--":
5602+
return out[i + 1 :], old_env, old_cwd
5603+
if a == "-C" or a.startswith("-C") and len(a) > 2:
5604+
if a == "-C":
5605+
if i + 1 >= len(out):
5606+
raise SystemExit("option requires an argument -- C")
5607+
path = out[i + 1]
5608+
i += 2
5609+
else:
5610+
path = a[2:]
5611+
i += 1
5612+
if path:
5613+
os.chdir(path)
5614+
continue
5615+
if a == "-c" or a.startswith("-c") and len(a) > 2:
5616+
if a == "-c":
5617+
if i + 1 >= len(out):
5618+
raise SystemExit("option requires an argument -- c")
5619+
value = out[i + 1]
5620+
i += 2
5621+
else:
5622+
value = a[2:]
5623+
i += 1
5624+
_remember_env(old_env, "GIT_CONFIG_PARAMETERS")
5625+
cur = os.environ.get("GIT_CONFIG_PARAMETERS", "")
5626+
os.environ["GIT_CONFIG_PARAMETERS"] = (cur + "\n" if cur else "") + value
5627+
continue
5628+
if a.startswith("--config-env="):
5629+
spec = a.split("=", 1)[1]
5630+
name, sep, env_name = spec.partition("=")
5631+
if not sep or env_name not in os.environ:
5632+
raise SystemExit(f"invalid --config-env: {spec}")
5633+
_remember_env(old_env, "GIT_CONFIG_PARAMETERS")
5634+
cur = os.environ.get("GIT_CONFIG_PARAMETERS", "")
5635+
os.environ["GIT_CONFIG_PARAMETERS"] = (cur + "\n" if cur else "") + f"{name}={os.environ[env_name]}"
5636+
i += 1
5637+
continue
5638+
if a == "--git-dir" or a.startswith("--git-dir="):
5639+
if a == "--git-dir":
5640+
if i + 1 >= len(out):
5641+
raise SystemExit("option requires an argument -- git-dir")
5642+
path = out[i + 1]
5643+
i += 2
5644+
else:
5645+
path = a.split("=", 1)[1]
5646+
i += 1
5647+
_set_temp_env(old_env, "GIT_DIR", _normalize_global_path(path))
5648+
continue
5649+
if a == "--work-tree" or a.startswith("--work-tree="):
5650+
if a == "--work-tree":
5651+
if i + 1 >= len(out):
5652+
raise SystemExit("option requires an argument -- work-tree")
5653+
path = out[i + 1]
5654+
i += 2
5655+
else:
5656+
path = a.split("=", 1)[1]
5657+
i += 1
5658+
_set_temp_env(old_env, "GIT_WORK_TREE", _normalize_global_path(path))
5659+
continue
5660+
if a == "--namespace" or a.startswith("--namespace="):
5661+
if a == "--namespace":
5662+
if i + 1 >= len(out):
5663+
raise SystemExit("option requires an argument -- namespace")
5664+
value = out[i + 1]
5665+
i += 2
5666+
else:
5667+
value = a.split("=", 1)[1]
5668+
i += 1
5669+
_set_temp_env(old_env, "GIT_NAMESPACE", value)
5670+
continue
5671+
if a == "--bare":
5672+
_set_temp_env(old_env, "GIT_DIR", _normalize_global_path("."))
5673+
i += 1
5674+
continue
5675+
if a in _GLOBAL_FLAGS_IGNORED:
5676+
i += 1
5677+
continue
5678+
if a in ("--exec-path", "--html-path", "--man-path", "--info-path"):
5679+
_print("")
5680+
return [_GLOBAL_EXIT], old_env, old_cwd
5681+
if a.startswith("--exec-path="):
5682+
_set_temp_env(old_env, "GIT_EXEC_PATH", a.split("=", 1)[1])
5683+
i += 1
5684+
continue
5685+
return out[i:], old_env, old_cwd
5686+
return [], old_env, old_cwd
5687+
5688+
55635689
def main(argv: Optional[list[str]] = None) -> int:
55645690
argv = list(sys.argv[1:]) if argv is None else list(argv)
5565-
if not argv or argv[0] in ("-h", "--help"):
5566-
return cmd_help([])
5567-
if argv[0] == "--version":
5568-
from . import __version__
5569-
_print(f"pygit version {__version__}")
5570-
return 0
5571-
cmd = argv[0]
5572-
rest = argv[1:]
5573-
fn = _COMMANDS.get(cmd)
5574-
if fn is None:
5575-
_err(f"pygit: '{cmd}' is not a pygit command. See 'pygit help'.")
5576-
return 1
55775691
try:
5692+
argv, old_env, old_cwd = _consume_global_options(argv)
5693+
except SystemExit as e:
5694+
_err(str(e))
5695+
return 129
5696+
try:
5697+
if not argv or argv[0] in ("-h", "--help"):
5698+
return cmd_help([])
5699+
if argv[0] == _GLOBAL_EXIT:
5700+
return 0
5701+
if argv[0] in ("--version", "-v", "version"):
5702+
from . import __version__
5703+
_print(f"pygit version {__version__}")
5704+
return 0
5705+
cmd = argv[0]
5706+
rest = argv[1:]
5707+
fn = _COMMANDS.get(cmd)
5708+
if fn is None:
5709+
_err(f"pygit: '{cmd}' is not a pygit command. See 'pygit help'.")
5710+
return 1
55785711
return fn(rest)
55795712
except RepositoryError as e:
55805713
_err(f"fatal: {e}")
55815714
return 128
55825715
except BrokenPipeError:
55835716
return 0
5717+
finally:
5718+
os.chdir(old_cwd)
5719+
_restore_env(old_env)

‎pythongit/repo.py‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,16 @@ def __init__(self, path: os.PathLike | str, *, gitdir: Optional[Path] = None, ba
3333

3434
@classmethod
3535
def discover(cls, start: os.PathLike | str = ".") -> "Repository":
36-
cur = Path(start).resolve()
36+
env_git_dir = os.environ.get("GIT_DIR")
37+
env_work_tree = os.environ.get("GIT_WORK_TREE")
38+
if env_git_dir:
39+
gitdir = Path(env_git_dir).resolve()
40+
if env_work_tree:
41+
return cls(Path(env_work_tree).resolve(), gitdir=gitdir)
42+
if gitdir.name == ".git":
43+
return cls(gitdir.parent, gitdir=gitdir)
44+
return cls(gitdir, gitdir=gitdir, bare=True)
45+
cur = Path(env_work_tree or start).resolve()
3746
for candidate in [cur, *cur.parents]:
3847
git = candidate / ".git"
3948
if git.is_dir():

‎tests/unit_integration.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,24 @@ def test_remote_verbose_option(tmprepo, capsys):
369369
assert "origin\thttps://example.com/repo.git (push)" in out
370370

371371

372+
def test_global_options_before_command(tmprepo, capsys):
373+
repo, path = tmprepo
374+
cwd = os.getcwd()
375+
try:
376+
os.chdir(path.parent)
377+
assert cli_run("-C", str(path), "--no-pager", "-c", "core.quotePath=false", "status", "--short", "--branch") == 0
378+
assert capsys.readouterr().out.startswith("## main\n")
379+
assert os.getcwd() == str(path.parent)
380+
381+
assert cli_run("--git-dir", str(repo.gitdir), "--work-tree", str(path), "status", "--short", "--branch") == 0
382+
assert capsys.readouterr().out.startswith("## main\n")
383+
384+
assert cli_run("version") == 0
385+
assert capsys.readouterr().out.startswith("pygit version ")
386+
finally:
387+
os.chdir(cwd)
388+
389+
372390
def test_describe_after_tag(tmprepo):
373391
from tests.conftest import commit_one
374392
repo, _ = tmprepo

0 commit comments

Comments
 (0)