Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 82b1452

Browse files
idk
1 parent 4561fc1 commit 82b1452

File tree

2 files changed

+80
-71
lines changed

2 files changed

+80
-71
lines changed

‎src/gitingest/clone.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99

1010
from gitingest.config import DEFAULT_TIMEOUT
1111
from gitingest.utils.git_utils import (
12-
_add_token_to_url,
1312
check_repo_exists,
1413
checkout_partial_clone,
1514
create_git_repo,
1615
ensure_git_installed,
16+
git_auth_context,
1717
is_github_host,
1818
resolve_commit,
1919
)
@@ -86,12 +86,7 @@ async def clone_repo(config: CloneConfig, *, token: str | None = None) -> None:
8686
commit = await resolve_commit(config, token=token)
8787
logger.debug("Resolved commit", extra={"commit": commit})
8888

89-
# Prepare URL with authentication if needed
90-
clone_url = url
91-
if token and is_github_host(url):
92-
clone_url = _add_token_to_url(url, token)
93-
94-
# Clone the repository using GitPython
89+
# Clone the repository using GitPython with proper authentication
9590
logger.info("Executing git clone operation", extra={"url": "<redacted>", "local_path": local_path})
9691
try:
9792
clone_kwargs = {
@@ -100,17 +95,22 @@ async def clone_repo(config: CloneConfig, *, token: str | None = None) -> None:
10095
"depth": 1,
10196
}
10297

103-
if partial_clone:
104-
# GitPython doesn't directly support --filter and --sparse in clone
105-
# We'll need to use git.Git() for the initial clone with these options
106-
git_cmd = git.Git()
107-
cmd_args = ["--single-branch", "--no-checkout", "--depth=1"]
98+
with git_auth_context(url, token) as git_cmd:
10899
if partial_clone:
109-
cmd_args.extend(["--filter=blob:none", "--sparse"])
110-
cmd_args.extend([clone_url, local_path])
111-
git_cmd.clone(*cmd_args)
112-
else:
113-
git.Repo.clone_from(clone_url, local_path, **clone_kwargs)
100+
# GitPython doesn't directly support --filter and --sparse in clone
101+
# We'll need to use git.Git() for the initial clone with these options
102+
cmd_args = ["--single-branch", "--no-checkout", "--depth=1"]
103+
if partial_clone:
104+
cmd_args.extend(["--filter=blob:none", "--sparse"])
105+
cmd_args.extend([url, local_path])
106+
git_cmd.clone(*cmd_args)
107+
# For regular clones without auth, we can use git.Repo.clone_from
108+
# But with auth, we need to use the configured git_cmd
109+
elif token and is_github_host(url):
110+
cmd_args = ["--single-branch", "--no-checkout", "--depth=1", url, local_path]
111+
git_cmd.clone(*cmd_args)
112+
else:
113+
git.Repo.clone_from(url, local_path, **clone_kwargs)
114114

115115
logger.info("Git clone completed successfully")
116116
except git.GitCommandError as exc:

‎src/gitingest/utils/git_utils.py

Lines changed: 63 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
import os
88
import re
99
import sys
10+
from contextlib import contextmanager, suppress
1011
from pathlib import Path
11-
from typing import TYPE_CHECKING, Final, Iterable
12-
from urllib.parse import urlparse, urlunparse
12+
from typing import TYPE_CHECKING, Final, Generator, Iterable
13+
from urllib.parse import urlparse
1314

1415
import git
1516

@@ -217,13 +218,6 @@ async def fetch_remote_branches_or_tags(url: str, *, ref_type: str, token: str |
217218

218219
# Use GitPython to get remote references
219220
try:
220-
git_cmd = git.Git()
221-
222-
# Prepare authentication if needed
223-
if token and is_github_host(url):
224-
auth_url = _add_token_to_url(url, token)
225-
url = auth_url
226-
227221
fetch_tags = ref_type == "tags"
228222
to_fetch = "tags" if fetch_tags else "heads"
229223

@@ -233,8 +227,9 @@ async def fetch_remote_branches_or_tags(url: str, *, ref_type: str, token: str |
233227
cmd_args.append("--refs") # Filter out peeled tag objects
234228
cmd_args.append(url)
235229

236-
# Run the command using git_cmd.ls_remote() method
237-
output = git_cmd.ls_remote(*cmd_args)
230+
# Run the command with proper authentication
231+
with git_auth_context(url, token) as git_cmd:
232+
output = git_cmd.ls_remote(*cmd_args)
238233

239234
# Parse output
240235
return [
@@ -318,6 +313,60 @@ def create_git_auth_header(token: str, url: str = "https://github.com") -> str:
318313
return f"http.https://{hostname}/.extraheader=Authorization: Basic {basic}"
319314

320315

316+
@contextmanager
317+
def git_auth_context(url: str, token: str | None = None) -> Generator[git.Git]:
318+
"""Context manager for GitPython authentication.
319+
320+
Creates a Git command object configured with authentication for GitHub repositories.
321+
Uses git's credential system instead of URL manipulation.
322+
323+
Parameters
324+
----------
325+
url : str
326+
The repository URL to check if authentication is needed.
327+
token : str | None
328+
GitHub personal access token (PAT) for accessing private repositories.
329+
330+
Yields
331+
------
332+
Generator[git.Git]
333+
Git command object configured with authentication.
334+
335+
"""
336+
git_cmd = git.Git()
337+
338+
if token and is_github_host(url):
339+
# Configure git to use the token for this hostname
340+
# This is equivalent to: git config credential.https://hostname.username x-oauth-basic
341+
# and: git config credential.https://hostname.helper store
342+
auth_header = create_git_auth_header(token, url)
343+
key, value = auth_header.split("=", 1)
344+
345+
# Set the auth configuration for this git command
346+
original_config = {}
347+
try:
348+
# Store original config if it exists
349+
with suppress(git.GitCommandError):
350+
original_config[key] = git_cmd.config("--get", key)
351+
352+
# Set the authentication
353+
git_cmd.config(key, value)
354+
355+
yield git_cmd
356+
357+
finally:
358+
# Restore original config
359+
try:
360+
if key in original_config:
361+
git_cmd.config(key, original_config[key])
362+
else:
363+
git_cmd.config("--unset", key)
364+
except git.GitCommandError:
365+
pass # Config cleanup failed, not critical
366+
else:
367+
yield git_cmd
368+
369+
321370
def validate_github_token(token: str) -> None:
322371
"""Validate the format of a GitHub Personal Access Token.
323372
@@ -419,15 +468,9 @@ async def _resolve_ref_to_sha(url: str, pattern: str, token: str | None = None)
419468
420469
"""
421470
try:
422-
git_cmd = git.Git()
423-
424-
# Prepare authentication if needed
425-
auth_url = url
426-
if token and is_github_host(url):
427-
auth_url = _add_token_to_url(url, token)
428-
429-
# Execute ls-remote command
430-
output = git_cmd.ls_remote(auth_url, pattern)
471+
# Execute ls-remote command with proper authentication
472+
with git_auth_context(url, token) as git_cmd:
473+
output = git_cmd.ls_remote(url, pattern)
431474
lines = output.splitlines()
432475

433476
sha = _pick_commit_sha(lines)
@@ -475,37 +518,3 @@ def _pick_commit_sha(lines: Iterable[str]) -> str | None:
475518
first_non_peeled = sha
476519

477520
return first_non_peeled # branch or lightweight tag (or None)
478-
479-
480-
def _add_token_to_url(url: str, token: str) -> str:
481-
"""Add authentication token to GitHub URL.
482-
483-
Parameters
484-
----------
485-
url : str
486-
The original GitHub URL.
487-
token : str
488-
The GitHub token to add.
489-
490-
Returns
491-
-------
492-
str
493-
The URL with embedded authentication.
494-
495-
"""
496-
parsed = urlparse(url)
497-
# Add token as username in URL (GitHub supports this)
498-
netloc = f"x-oauth-basic:{token}@{parsed.hostname}"
499-
if parsed.port:
500-
netloc += f":{parsed.port}"
501-
502-
return urlunparse(
503-
(
504-
parsed.scheme,
505-
netloc,
506-
parsed.path,
507-
parsed.params,
508-
parsed.query,
509-
parsed.fragment,
510-
),
511-
)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /