diff --git a/src/pkgmgr/actions/repository/create.py b/src/pkgmgr/actions/repository/create.py index 6aeb601..8c89d78 100644 --- a/src/pkgmgr/actions/repository/create.py +++ b/src/pkgmgr/actions/repository/create.py @@ -1,8 +1,8 @@ +# src/pkgmgr/actions/repository/create.py from __future__ import annotations import os import re -import subprocess from dataclasses import dataclass from typing import Any, Dict, Optional, Tuple from urllib.parse import urlparse @@ -14,6 +14,16 @@ from pkgmgr.actions.mirror.setup_cmd import setup_mirrors from pkgmgr.actions.repository.scaffold import render_default_templates from pkgmgr.core.command.alias import generate_alias from pkgmgr.core.config.save import save_user_config +from pkgmgr.core.git.commands import ( + GitCommitError, + GitPushUpstreamError, + add_all, + branch_move, + commit, + init, + push_upstream, +) +from pkgmgr.core.git.queries import get_config_value Repository = Dict[str, Any] @@ -28,27 +38,6 @@ class RepoParts: name: str -def _run(cmd: str, cwd: str, preview: bool) -> None: - if preview: - print(f"[Preview] Would run in {cwd}: {cmd}") - return - subprocess.run(cmd, cwd=cwd, shell=True, check=True) - - -def _git_get(key: str) -> str: - try: - out = subprocess.run( - f"git config --get {key}", - shell=True, - check=False, - capture_output=True, - text=True, - ) - return (out.stdout or "").strip() - except Exception: - return "" - - def _split_host_port(host_with_port: str) -> Tuple[str, Optional[str]]: if ":" in host_with_port: host, port = host_with_port.split(":", 1) @@ -116,28 +105,27 @@ def _write_default_mirrors(repo_dir: str, primary: str, name: str, preview: bool def _git_init_and_initial_commit(repo_dir: str, preview: bool) -> None: - _run("git init", cwd=repo_dir, preview=preview) - _run("git add -A", cwd=repo_dir, preview=preview) + init(cwd=repo_dir, preview=preview) + add_all(cwd=repo_dir, preview=preview) - if preview: - print(f'[Preview] Would run in {repo_dir}: git commit -m "Initial commit"') - return - - subprocess.run('git commit -m "Initial commit"', cwd=repo_dir, shell=True, check=False) + try: + commit("Initial commit", cwd=repo_dir, preview=preview) + except GitCommitError as exc: + print(f"[WARN] Initial commit failed (continuing): {exc}") def _git_push_main_or_master(repo_dir: str, preview: bool) -> None: - _run("git branch -M main", cwd=repo_dir, preview=preview) try: - _run("git push -u origin main", cwd=repo_dir, preview=preview) + branch_move("main", cwd=repo_dir, preview=preview) + push_upstream("origin", "main", cwd=repo_dir, preview=preview) return - except subprocess.CalledProcessError: + except GitPushUpstreamError: pass try: - _run("git branch -M master", cwd=repo_dir, preview=preview) - _run("git push -u origin master", cwd=repo_dir, preview=preview) - except subprocess.CalledProcessError as exc: + branch_move("master", cwd=repo_dir, preview=preview) + push_upstream("origin", "master", cwd=repo_dir, preview=preview) + except GitPushUpstreamError as exc: print(f"[WARN] Push failed: {exc}") @@ -157,8 +145,8 @@ def create_repo( base_dir = os.path.expanduser(str(directories.get("repositories", "~/Repositories"))) repo_dir = os.path.join(base_dir, parts.host, parts.owner, parts.name) - author_name = _git_get("user.name") or "Unknown Author" - author_email = _git_get("user.email") or "unknown@example.invalid" + author_name = get_config_value("user.name") or "Unknown Author" + author_email = get_config_value("user.email") or "unknown@example.invalid" homepage = _repo_homepage(parts.host, parts.owner, parts.name) primary_url = _build_default_primary_url(parts) diff --git a/src/pkgmgr/core/git/commands/__init__.py b/src/pkgmgr/core/git/commands/__init__.py index 37db521..cf8aab4 100644 --- a/src/pkgmgr/core/git/commands/__init__.py +++ b/src/pkgmgr/core/git/commands/__init__.py @@ -1,27 +1,31 @@ +# src/pkgmgr/core/git/commands/__init__.py from __future__ import annotations from .add import GitAddError, add +from .add_all import GitAddAllError, add_all +from .add_remote import GitAddRemoteError, add_remote +from .add_remote_push_url import GitAddRemotePushUrlError, add_remote_push_url +from .branch_move import GitBranchMoveError, branch_move from .checkout import GitCheckoutError, checkout +from .clone import GitCloneError, clone from .commit import GitCommitError, commit from .create_branch import GitCreateBranchError, create_branch from .delete_local_branch import GitDeleteLocalBranchError, delete_local_branch from .delete_remote_branch import GitDeleteRemoteBranchError, delete_remote_branch from .fetch import GitFetchError, fetch +from .init import GitInitError, init from .merge_no_ff import GitMergeError, merge_no_ff from .pull import GitPullError, pull from .pull_ff_only import GitPullFfOnlyError, pull_ff_only from .push import GitPushError, push from .push_upstream import GitPushUpstreamError, push_upstream - -from .add_remote import GitAddRemoteError, add_remote -from .add_remote_push_url import GitAddRemotePushUrlError, add_remote_push_url from .set_remote_url import GitSetRemoteUrlError, set_remote_url from .tag_annotated import GitTagAnnotatedError, tag_annotated from .tag_force_annotated import GitTagForceAnnotatedError, tag_force_annotated -from .clone import GitCloneError, clone __all__ = [ "add", + "add_all", "fetch", "checkout", "pull", @@ -39,7 +43,10 @@ __all__ = [ "tag_annotated", "tag_force_annotated", "clone", + "init", + "branch_move", "GitAddError", + "GitAddAllError", "GitFetchError", "GitCheckoutError", "GitPullError", @@ -57,4 +64,6 @@ __all__ = [ "GitTagAnnotatedError", "GitTagForceAnnotatedError", "GitCloneError", + "GitInitError", + "GitBranchMoveError", ] diff --git a/src/pkgmgr/core/git/commands/add_all.py b/src/pkgmgr/core/git/commands/add_all.py new file mode 100644 index 0000000..c03b5b1 --- /dev/null +++ b/src/pkgmgr/core/git/commands/add_all.py @@ -0,0 +1,22 @@ +# src/pkgmgr/core/git/commands/add_all.py +from __future__ import annotations + +from ..errors import GitError, GitCommandError +from ..run import run + + +class GitAddAllError(GitCommandError): + """Raised when `git add -A` fails.""" + + +def add_all(*, cwd: str = ".", preview: bool = False) -> None: + """ + Stage all changes (tracked + untracked). + + Equivalent to: + git add -A + """ + try: + run(["add", "-A"], cwd=cwd, preview=preview) + except GitError as exc: + raise GitAddAllError("Failed to stage all changes with `git add -A`.", cwd=cwd) from exc diff --git a/src/pkgmgr/core/git/commands/branch_move.py b/src/pkgmgr/core/git/commands/branch_move.py new file mode 100644 index 0000000..a8fa38f --- /dev/null +++ b/src/pkgmgr/core/git/commands/branch_move.py @@ -0,0 +1,22 @@ +# src/pkgmgr/core/git/commands/branch_move.py +from __future__ import annotations + +from ..errors import GitError, GitCommandError +from ..run import run + + +class GitBranchMoveError(GitCommandError): + """Raised when renaming/moving a branch fails.""" + + +def branch_move(branch: str, *, cwd: str = ".", preview: bool = False) -> None: + """ + Rename the current branch to `branch`, creating it if needed. + + Equivalent to: + git branch -M + """ + try: + run(["branch", "-M", branch], cwd=cwd, preview=preview) + except GitError as exc: + raise GitBranchMoveError(f"Failed to move/rename current branch to {branch!r}.", cwd=cwd) from exc diff --git a/src/pkgmgr/core/git/commands/init.py b/src/pkgmgr/core/git/commands/init.py new file mode 100644 index 0000000..5a592de --- /dev/null +++ b/src/pkgmgr/core/git/commands/init.py @@ -0,0 +1,22 @@ +# src/pkgmgr/core/git/commands/init.py +from __future__ import annotations + +from ..errors import GitError, GitCommandError +from ..run import run + + +class GitInitError(GitCommandError): + """Raised when `git init` fails.""" + + +def init(*, cwd: str = ".", preview: bool = False) -> None: + """ + Initialize a repository. + + Equivalent to: + git init + """ + try: + run(["init"], cwd=cwd, preview=preview) + except GitError as exc: + raise GitInitError("Failed to initialize git repository.", cwd=cwd) from exc diff --git a/src/pkgmgr/core/git/commands/push_upstream.py b/src/pkgmgr/core/git/commands/push_upstream.py index 9c6ab82..dc04c5d 100644 --- a/src/pkgmgr/core/git/commands/push_upstream.py +++ b/src/pkgmgr/core/git/commands/push_upstream.py @@ -1,3 +1,4 @@ +# src/pkgmgr/core/git/commands/push_upstream.py from __future__ import annotations from ..errors import GitError, GitCommandError @@ -8,14 +9,21 @@ class GitPushUpstreamError(GitCommandError): """Raised when pushing a branch with upstream tracking fails.""" -def push_upstream(remote: str, branch: str, cwd: str = ".") -> None: +def push_upstream( + remote: str, + branch: str, + *, + cwd: str = ".", + preview: bool = False, +) -> None: """ Push a branch and set upstream tracking. - Equivalent to: git push -u + Equivalent to: + git push -u """ try: - run(["push", "-u", remote, branch], cwd=cwd) + run(["push", "-u", remote, branch], cwd=cwd, preview=preview) except GitError as exc: raise GitPushUpstreamError( f"Failed to push branch {branch!r} to {remote!r} with upstream tracking.",