Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/gitingest/clone.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,32 @@ async def clone_repo(config: CloneConfig, *, token: str | None = None) -> None:
}

with git_auth_context(url, token) as (git_cmd, auth_url):
# Determine branch/tag flag for --single-branch to fetch the correct ref
branch_args: list[str] = []
if config.branch:
branch_args = ["-b", config.branch]
elif config.tag:
branch_args = ["-b", config.tag]

if partial_clone:
# For partial clones, use git.Git() with filter and sparse options
cmd_args = ["--single-branch", "--no-checkout", "--depth=1"]
cmd_args.extend(branch_args)
cmd_args.extend(["--filter=blob:none", "--sparse"])
cmd_args.extend([auth_url, local_path])
git_cmd.clone(*cmd_args)
elif token and is_github_host(url):
# For authenticated GitHub repos, use git_cmd with auth URL
cmd_args = ["--single-branch", "--no-checkout", "--depth=1", auth_url, local_path]
cmd_args = ["--single-branch", "--no-checkout", "--depth=1"]
cmd_args.extend(branch_args)
cmd_args.extend([auth_url, local_path])
git_cmd.clone(*cmd_args)
else:
# For non-authenticated repos, use the standard GitPython method
if config.branch:
clone_kwargs["branch"] = config.branch
elif config.tag:
clone_kwargs["branch"] = config.tag
git.Repo.clone_from(url, local_path, **clone_kwargs)

logger.info("Git clone completed successfully")
Expand Down
46 changes: 46 additions & 0 deletions tests/test_clone.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,52 @@ async def test_clone_with_include_submodules(gitpython_mocks: dict) -> None:
mock_repo.git.submodule.assert_called_with("update", "--init", "--recursive", "--depth=1")


@pytest.mark.asyncio
async def test_clone_with_branch_passes_branch_to_git(repo_exists_true: AsyncMock, gitpython_mocks: dict) -> None:
"""Test that cloning with a branch passes -b to the git clone command.

Given a valid URL and a specific branch:
When ``clone_repo`` is called,
Then the clone command should include the branch so --single-branch fetches the correct ref.
"""
clone_config = CloneConfig(url=DEMO_URL, local_path=LOCAL_REPO_PATH, commit=None, branch="feature-branch")

await clone_repo(clone_config)

mock_clone_from = gitpython_mocks["clone_from"]
mock_clone_from.assert_called_once()

_, kwargs = mock_clone_from.call_args
assert kwargs.get("branch") == "feature-branch", (
"clone_from should receive branch='feature-branch' so --single-branch fetches the correct ref"
)


@pytest.mark.asyncio
async def test_clone_with_branch_and_token_passes_branch_flag(
repo_exists_true: AsyncMock, gitpython_mocks: dict
) -> None:
"""Test that cloning with a branch and token passes -b to git.Git().clone().

Given a GitHub URL, a token, and a specific branch:
When ``clone_repo`` is called,
Then the raw git clone args should include -b <branch>.
"""
clone_config = CloneConfig(
url=DEMO_URL, local_path=LOCAL_REPO_PATH, commit=None, branch="feature-branch"
)

await clone_repo(clone_config, token="ghp_testtoken123")

mock_git_cmd = gitpython_mocks["git_cmd"]
mock_git_cmd.clone.assert_called_once()

clone_args = mock_git_cmd.clone.call_args[0]
assert "-b" in clone_args, "clone args should include -b flag"
b_index = list(clone_args).index("-b")
assert clone_args[b_index + 1] == "feature-branch", "branch name should follow -b flag"


@pytest.mark.asyncio
async def test_check_repo_exists_with_auth_token(mocker: MockerFixture) -> None:
"""Test ``check_repo_exists`` with authentication token.
Expand Down