Skip to content

Commit 9b59aaa

Browse files
committed
feat(git): enhance git init support with all options and tests
- Add support for all git-init options (template, separate_git_dir, object_format, etc.) - Add comprehensive tests for each option - Fix path handling for separate_git_dir - Fix string formatting for bytes paths - Update docstrings with examples for all options
1 parent 8ca862d commit 9b59aaa

File tree

2 files changed

+184
-31
lines changed

2 files changed

+184
-31
lines changed

src/libvcs/cmd/git.py

Lines changed: 71 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,7 @@ def init(
10351035
object_format: t.Literal["sha1", "sha256"] | None = None,
10361036
branch: str | None = None,
10371037
initial_branch: str | None = None,
1038-
shared: bool | None = None,
1038+
shared: bool | str | None = None,
10391039
quiet: bool | None = None,
10401040
bare: bool | None = None,
10411041
# libvcs special behavior
@@ -1046,60 +1046,100 @@ def init(
10461046
10471047
Parameters
10481048
----------
1049-
quiet : bool
1050-
``--quiet``
1051-
bare : bool
1052-
``--bare``
1053-
object_format :
1054-
Hash algorithm used for objects. SHA-256 is still experimental as of git
1055-
2.36.0.
1049+
template : str, optional
1050+
Directory from which templates will be used. The template directory
1051+
contains files and directories that will be copied to the $GIT_DIR
1052+
after it is created.
1053+
separate_git_dir : :attr:`libvcs._internal.types.StrOrBytesPath`, optional
1054+
Instead of placing the git repository in <directory>/.git/, place it in
1055+
the specified path.
1056+
object_format : "sha1" | "sha256", optional
1057+
Specify the hash algorithm to use. The default is sha1. Note that
1058+
sha256 is still experimental in git.
1059+
branch : str, optional
1060+
Use the specified name for the initial branch. If not specified, fall
1061+
back to the default name (currently "master").
1062+
initial_branch : str, optional
1063+
Alias for branch parameter. Specify the name for the initial branch.
1064+
shared : bool | str, optional
1065+
Specify that the git repository is to be shared amongst several users.
1066+
Can be 'false', 'true', 'umask', 'group', 'all', 'world',
1067+
'everybody', or an octal number.
1068+
quiet : bool, optional
1069+
Only print error and warning messages; all other output will be
1070+
suppressed.
1071+
bare : bool, optional
1072+
Create a bare repository. If GIT_DIR environment is not set, it is set
1073+
to the current working directory.
10561074
10571075
Examples
10581076
--------
1059-
>>> new_repo = tmp_path / 'example'
1060-
>>> new_repo.mkdir()
1061-
>>> git = Git(path=new_repo)
1077+
>>> git = Git(path=tmp_path)
10621078
>>> git.init()
10631079
'Initialized empty Git repository in ...'
1064-
>>> pathlib.Path(new_repo / 'test').write_text('foo', 'utf-8')
1065-
3
1066-
>>> git.run(['add', '.'])
1067-
''
10681080
1069-
Bare:
1081+
Create with a specific initial branch name:
10701082
1071-
>>> new_repo = tmp_path / 'example1'
1083+
>>> new_repo = tmp_path / 'branch_example'
10721084
>>> new_repo.mkdir()
10731085
>>> git = Git(path=new_repo)
1086+
>>> git.init(branch='main')
1087+
'Initialized empty Git repository in ...'
1088+
1089+
Create a bare repository:
1090+
1091+
>>> bare_repo = tmp_path / 'bare_example'
1092+
>>> bare_repo.mkdir()
1093+
>>> git = Git(path=bare_repo)
10741094
>>> git.init(bare=True)
10751095
'Initialized empty Git repository in ...'
1076-
>>> pathlib.Path(new_repo / 'HEAD').exists()
1077-
True
10781096
1079-
Existing repo:
1097+
Create with a separate git directory:
10801098
1081-
>>> git = Git(path=new_repo)
1082-
>>> git = Git(path=example_git_repo.path)
1083-
>>> git_remote_repo = create_git_remote_repo()
1084-
>>> git.init()
1085-
'Reinitialized existing Git repository in ...'
1099+
>>> repo_path = tmp_path / 'repo'
1100+
>>> git_dir = tmp_path / 'git_dir'
1101+
>>> repo_path.mkdir()
1102+
>>> git_dir.mkdir()
1103+
>>> git = Git(path=repo_path)
1104+
>>> git.init(separate_git_dir=str(git_dir.absolute()))
1105+
'Initialized empty Git repository in ...'
1106+
1107+
Create with shared permissions:
10861108
1109+
>>> shared_repo = tmp_path / 'shared_example'
1110+
>>> shared_repo.mkdir()
1111+
>>> git = Git(path=shared_repo)
1112+
>>> git.init(shared='group')
1113+
'Initialized empty shared Git repository in ...'
1114+
1115+
Create with a template directory:
1116+
1117+
>>> template_repo = tmp_path / 'template_example'
1118+
>>> template_repo.mkdir()
1119+
>>> git = Git(path=template_repo)
1120+
>>> git.init(template=str(tmp_path))
1121+
'Initialized empty Git repository in ...'
10871122
"""
1088-
required_flags: list[str] = [str(self.path)]
10891123
local_flags: list[str] = []
1124+
required_flags: list[str] = [str(self.path)]
10901125

10911126
if template is not None:
10921127
local_flags.append(f"--template={template}")
10931128
if separate_git_dir is not None:
1094-
local_flags.append(f"--separate-git-dir={separate_git_dir!r}")
1129+
if isinstance(separate_git_dir, pathlib.Path):
1130+
separate_git_dir = str(separate_git_dir.absolute())
1131+
local_flags.append(f"--separate-git-dir={separate_git_dir!s}")
10951132
if object_format is not None:
10961133
local_flags.append(f"--object-format={object_format}")
10971134
if branch is not None:
1098-
local_flags.extend(["--branch", branch])
1099-
if initial_branch is not None:
1135+
local_flags.extend(["--initial-branch", branch])
1136+
elif initial_branch is not None:
11001137
local_flags.extend(["--initial-branch", initial_branch])
1101-
if shared is True:
1102-
local_flags.append("--shared")
1138+
if shared is not None:
1139+
if isinstance(shared, bool):
1140+
local_flags.append("--shared")
1141+
else:
1142+
local_flags.append(f"--shared={shared}")
11031143
if quiet is True:
11041144
local_flags.append("--quiet")
11051145
if bare is True:

tests/cmd/test_git.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,116 @@ def test_git_constructor(
1919
repo = git.Git(path=path_type(tmp_path))
2020

2121
assert repo.path == tmp_path
22+
23+
24+
def test_git_init_basic(tmp_path: pathlib.Path) -> None:
25+
"""Test basic git init functionality."""
26+
repo = git.Git(path=tmp_path)
27+
result = repo.init()
28+
assert "Initialized empty Git repository" in result
29+
assert (tmp_path / ".git").is_dir()
30+
31+
32+
def test_git_init_bare(tmp_path: pathlib.Path) -> None:
33+
"""Test git init with bare repository."""
34+
repo = git.Git(path=tmp_path)
35+
result = repo.init(bare=True)
36+
assert "Initialized empty Git repository" in result
37+
# Bare repos have files directly in the directory
38+
assert (tmp_path / "HEAD").exists()
39+
40+
41+
def test_git_init_template(tmp_path: pathlib.Path) -> None:
42+
"""Test git init with template directory."""
43+
template_dir = tmp_path / "template"
44+
template_dir.mkdir()
45+
(template_dir / "hooks").mkdir()
46+
(template_dir / "hooks" / "pre-commit").write_text("#!/bin/sh\nexit 0\n")
47+
48+
repo_dir = tmp_path / "repo"
49+
repo_dir.mkdir()
50+
repo = git.Git(path=repo_dir)
51+
result = repo.init(template=str(template_dir))
52+
53+
assert "Initialized empty Git repository" in result
54+
assert (repo_dir / ".git" / "hooks" / "pre-commit").exists()
55+
56+
57+
def test_git_init_separate_git_dir(tmp_path: pathlib.Path) -> None:
58+
"""Test git init with separate git directory."""
59+
repo_dir = tmp_path / "repo"
60+
git_dir = tmp_path / "git_dir"
61+
repo_dir.mkdir()
62+
git_dir.mkdir()
63+
64+
repo = git.Git(path=repo_dir)
65+
result = repo.init(separate_git_dir=str(git_dir.absolute()))
66+
67+
assert "Initialized empty Git repository" in result
68+
assert git_dir.is_dir()
69+
assert (git_dir / "HEAD").exists()
70+
71+
72+
def test_git_init_initial_branch(tmp_path: pathlib.Path) -> None:
73+
"""Test git init with custom initial branch name."""
74+
repo = git.Git(path=tmp_path)
75+
result = repo.init(branch="main")
76+
77+
assert "Initialized empty Git repository" in result
78+
# Check if HEAD points to the correct branch
79+
head_content = (tmp_path / ".git" / "HEAD").read_text()
80+
assert "ref: refs/heads/main" in head_content
81+
82+
83+
def test_git_init_shared(tmp_path: pathlib.Path) -> None:
84+
"""Test git init with shared repository settings."""
85+
repo = git.Git(path=tmp_path)
86+
87+
# Test boolean shared
88+
result = repo.init(shared=True)
89+
assert "Initialized empty shared Git repository" in result
90+
91+
# Test string shared value
92+
repo_dir = tmp_path / "shared_group"
93+
repo_dir.mkdir()
94+
repo = git.Git(path=repo_dir)
95+
result = repo.init(shared="group")
96+
assert "Initialized empty shared Git repository" in result
97+
98+
99+
def test_git_init_quiet(tmp_path: pathlib.Path) -> None:
100+
"""Test git init with quiet flag."""
101+
repo = git.Git(path=tmp_path)
102+
result = repo.init(quiet=True)
103+
# Quiet mode should suppress normal output
104+
assert result == "" or "Initialized empty Git repository" not in result
105+
106+
107+
def test_git_init_object_format(tmp_path: pathlib.Path) -> None:
108+
"""Test git init with different object formats."""
109+
repo = git.Git(path=tmp_path)
110+
111+
# Test with sha1 (default)
112+
result = repo.init(object_format="sha1")
113+
assert "Initialized empty Git repository" in result
114+
115+
# Note: sha256 test is commented out as it might not be supported in all
116+
# git versions
117+
# repo_dir = tmp_path / "sha256"
118+
# repo_dir.mkdir()
119+
# repo = git.Git(path=repo_dir)
120+
# result = repo.init(object_format="sha256")
121+
# assert "Initialized empty Git repository" in result
122+
123+
124+
def test_git_reinit(tmp_path: pathlib.Path) -> None:
125+
"""Test reinitializing an existing repository."""
126+
repo = git.Git(path=tmp_path)
127+
128+
# Initial init
129+
first_result = repo.init()
130+
assert "Initialized empty Git repository" in first_result
131+
132+
# Reinit
133+
second_result = repo.init()
134+
assert "Reinitialized existing Git repository" in second_result

0 commit comments

Comments
 (0)