diff --git a/CHANGELOG.md b/CHANGELOG.md index c6390c6..4e68a13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ Versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- **Dynamic Branch Initialization:** `gitgo link` and core init routines now dynamically resolve the default branch name. It respects your global `init.defaultBranch` setting, falls back to the `gitgo.default-branch` config, and defaults to `main` if neither is found. + --- ## [1.7.0] - 2026-06-02 diff --git a/assets/demo.gif b/assets/demo.gif index 29b276d..5bf69f4 100644 Binary files a/assets/demo.gif and b/assets/demo.gif differ diff --git a/pyproject.toml b/pyproject.toml index 58ab7d0..90225b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pygitgo" -version = "1.7.0" +version = "1.7.1" description = "GitGo CLI - Your Fast Git Companion. Simplifies git push, link, stash, and user management." readme = "README.md" license = {text = "GPL-3.0-or-later"} diff --git a/src/pygitgo/commands/git_core.py b/src/pygitgo/commands/git_core.py index 1115e96..85767e6 100644 --- a/src/pygitgo/commands/git_core.py +++ b/src/pygitgo/commands/git_core.py @@ -1,9 +1,9 @@ from pygitgo.auth.ssh_utils import convert_https_to_ssh, get_ssh_key_path, is_ssh_url, check_connection from pygitgo.utils.colors import info, success, warning, error from pygitgo.exceptions import GitGoError, GitCommandError -from pygitgo.utils.executor import run_command -from pygitgo.utils.config import get_config from pygitgo.commands.git_remote import handle_rebase +from pygitgo.utils.config import get_default_branch +from pygitgo.utils.executor import run_command import os @@ -43,7 +43,7 @@ def git_init(): warning("Already a git repository! Skipping init...") return False - default_main_branch = get_config("default-branch", "main") + default_main_branch = get_default_branch() try: run_command(["git", "init", "-b", default_main_branch], loading_msg="Initializing git repository...") diff --git a/src/pygitgo/commands/link.py b/src/pygitgo/commands/link.py index 1ac4743..b37b5d1 100644 --- a/src/pygitgo/commands/link.py +++ b/src/pygitgo/commands/link.py @@ -1,11 +1,11 @@ -from pygitgo.utils.colors import success, warning, error, highlight, info, print_banner from pygitgo.commands.git_remote import add_remote_origin, confirm_remote_link +from pygitgo.utils.colors import success, warning, error, info, print_banner from pygitgo.commands.git_core import git_init, git_commit, git_push from pygitgo.commands.git_branch import get_current_branch from pygitgo.exceptions import GitCommandError, GitGoError -from pygitgo.utils.executor import run_command -from pygitgo.utils.config import get_config from pygitgo.utils.validators import validate_repo_url +from pygitgo.utils.config import get_default_branch +from pygitgo.utils.executor import run_command import sys @@ -42,10 +42,6 @@ def link_operation(args): commit_message = args.message - highlight("INITIATING LINK OPERATION...") - highlight(f"Target: {repo_url}") - print() - initialized = False committed = False remote_added = False @@ -76,7 +72,7 @@ def link_operation(args): return current_branch = get_current_branch() - main_branch = get_config("default-branch", "main") + main_branch = get_default_branch() if current_branch != main_branch: run_command(["git", "branch", "-m", main_branch], loading_msg=f"Renaming branch '{current_branch}' to '{main_branch}'...") @@ -100,8 +96,6 @@ def link_operation(args): warning(f"Then: gitgo push {main_branch} 'your message'\n") return - print_banner("REPOSITORY INITIALIZED AND LINKED.") - git_push(current_branch) print_banner("REPOSITORY INITIALIZED AND DEPLOYED.") diff --git a/src/pygitgo/main.py b/src/pygitgo/main.py index e89fcd1..5df88f2 100644 --- a/src/pygitgo/main.py +++ b/src/pygitgo/main.py @@ -1,4 +1,4 @@ -from pygitgo.utils.update_checker import check_for_updates_background +from pygitgo.utils.update_checker import check_for_updates_background, check_for_updates from pygitgo.utils.bootstrap import ensure_first_run_setup from pygitgo.utils.colors import info, warning, error from pygitgo.commands.config import config_operation @@ -147,12 +147,15 @@ def main(): args = parser.parse_args() if getattr(args, 'version', False): - print(f"GitGo {get_version()}") + current_v = get_version() + print(f"GitGo {current_v}") print(f"Support GitGo: https://ko-fi.com/huerte") + check_for_updates(current_v) return if args.ready: info("ALL UNITS ONLINE. GitGo STANDING BY. AWAITING COMMANDS...") + check_for_updates(get_version()) return if not args.command: diff --git a/src/pygitgo/utils/config.py b/src/pygitgo/utils/config.py index dfeb319..f83f5d8 100644 --- a/src/pygitgo/utils/config.py +++ b/src/pygitgo/utils/config.py @@ -1,29 +1,44 @@ -from pygitgo.utils.executor import run_command -from pygitgo.utils.colors import error, warning, success, info -from pygitgo.exceptions import GitCommandError - - -def get_config(key, fallback_value): - - config_key = f"gitgo.{key}" - - try: - result = run_command(['git', 'config', '--global', config_key]) - return result.strip() - except GitCommandError: - return fallback_value - - -def set_config(key, value): - - config_key = f"gitgo.{key}" - - try: - result = run_command(['git', 'config', '--global', config_key, value]) - success(f"\nConfiguration saved: {key} = '{value}'") - return True - except GitCommandError: - error(f"\nFailed to save configuration for '{key}'.") - return False - - \ No newline at end of file +from pygitgo.utils.executor import run_command +from pygitgo.utils.colors import error, warning, success, info +from pygitgo.exceptions import GitCommandError +import subprocess + + +def get_config(key, fallback_value): + + config_key = f"gitgo.{key}" + + try: + result = run_command(['git', 'config', '--global', config_key]) + return result.strip() + except GitCommandError: + return fallback_value + + +def set_config(key, value): + + config_key = f"gitgo.{key}" + + try: + result = run_command(['git', 'config', '--global', config_key, value]) + success(f"\nConfiguration saved: {key} = '{value}'") + return True + except GitCommandError: + error(f"\nFailed to save configuration for '{key}'.") + return False + + +def get_default_branch() -> str: + try: + branch = subprocess.check_output( + ["git", "config", "--get", "init.defaultBranch"], + text=True, + stderr=subprocess.DEVNULL, + ).strip() + if branch: + return branch + except (subprocess.CalledProcessError, FileNotFoundError): + pass + + return get_config("default-branch", "main") + diff --git a/tests/test_config_utils.py b/tests/test_config_utils.py index 3b93598..f665d24 100644 --- a/tests/test_config_utils.py +++ b/tests/test_config_utils.py @@ -1,4 +1,4 @@ -from pygitgo.utils.config import get_config, set_config +from pygitgo.utils.config import get_config, set_config, get_default_branch import subprocess @@ -55,3 +55,50 @@ def test_get_config_error_handling(mocker): fake_run = mocker.patch('pygitgo.utils.config.run_command', side_effect=GitCommandError(['git'])) result = get_config("default-key", "fallback") assert result == "fallback" + + +# --- get_default_branch tests --- + +def test_get_default_branch_uses_git_init_default_branch(mocker): + # Level 1: native git config wins over gitgo config and hard fallback. + mocker.patch( + "pygitgo.utils.config.subprocess.check_output", + return_value="develop\n", + ) + result = get_default_branch() + assert result == "develop" + + +def test_get_default_branch_falls_back_to_gitgo_config(mocker): + # Level 2: init.defaultBranch unset, gitgo.default-branch is set. + mocker.patch( + "pygitgo.utils.config.subprocess.check_output", + side_effect=subprocess.CalledProcessError(1, "git"), + ) + mocker.patch("pygitgo.utils.config.run_command", return_value="trunk") + result = get_default_branch() + assert result == "trunk" + + +def test_get_default_branch_hard_fallback_to_main(mocker): + # Level 3: both sources absent, returns "main". + mocker.patch( + "pygitgo.utils.config.subprocess.check_output", + side_effect=subprocess.CalledProcessError(1, "git"), + ) + from pygitgo.exceptions import GitCommandError + mocker.patch("pygitgo.utils.config.run_command", side_effect=GitCommandError(["git"])) + result = get_default_branch() + assert result == "main" + + +def test_get_default_branch_handles_git_not_found(mocker): + # FileNotFoundError when git is not on PATH should not crash. + mocker.patch( + "pygitgo.utils.config.subprocess.check_output", + side_effect=FileNotFoundError, + ) + from pygitgo.exceptions import GitCommandError + mocker.patch("pygitgo.utils.config.run_command", side_effect=GitCommandError(["git"])) + result = get_default_branch() + assert result == "main" diff --git a/tests/test_git_core.py b/tests/test_git_core.py index 048fb14..ffa208d 100644 --- a/tests/test_git_core.py +++ b/tests/test_git_core.py @@ -30,7 +30,7 @@ def test_git_init_already_initialized(mocker): def test_git_init_success(mocker): mocker.patch('os.path.isdir', return_value=False) - mocker.patch('pygitgo.commands.git_core.get_config', return_value='main') + mocker.patch('pygitgo.commands.git_core.get_default_branch', return_value='main') fake_success = mocker.patch('pygitgo.commands.git_core.success') fake_run = mocker.patch('pygitgo.commands.git_core.run_command', return_value='ok') @@ -46,7 +46,7 @@ def test_git_init_success(mocker): def test_git_init_fallback(mocker): from pygitgo.exceptions import GitCommandError mocker.patch('os.path.isdir', return_value=False) - mocker.patch('pygitgo.commands.git_core.get_config', return_value='main') + mocker.patch('pygitgo.commands.git_core.get_default_branch', return_value='main') fake_success = mocker.patch('pygitgo.commands.git_core.success') fake_run = mocker.patch( diff --git a/tests/test_link.py b/tests/test_link.py index 0b7a8b9..4891235 100644 --- a/tests/test_link.py +++ b/tests/test_link.py @@ -35,10 +35,10 @@ def test_link_new_repo_no_remote_refs(mocker): fake_add_remote = mocker.patch("pygitgo.commands.link.add_remote_origin") mocker.patch("pygitgo.commands.link.confirm_remote_link", return_value=True) mocker.patch("pygitgo.commands.link.get_current_branch", return_value="master") - mocker.patch("pygitgo.commands.link.get_config", return_value="main") + mocker.patch("pygitgo.commands.link.get_default_branch", return_value="main") fake_push = mocker.patch("pygitgo.commands.link.git_push") - fake_run = mocker.patch("pygitgo.commands.link.run_command", return_value="") # remote_refs is empty + fake_run = mocker.patch("pygitgo.commands.link.run_command", return_value="") args = Namespace(url="git@github.com:user/repo.git", message="Initial commit") link_operation(args) @@ -57,7 +57,7 @@ def test_link_new_repo_with_remote_refs_pull_success(mocker): mocker.patch("pygitgo.commands.link.add_remote_origin") mocker.patch("pygitgo.commands.link.confirm_remote_link", return_value=True) mocker.patch("pygitgo.commands.link.get_current_branch", return_value="main") - mocker.patch("pygitgo.commands.link.get_config", return_value="main") + mocker.patch("pygitgo.commands.link.get_default_branch", return_value="main") fake_push = mocker.patch("pygitgo.commands.link.git_push") fake_success = mocker.patch("pygitgo.commands.link.success") @@ -87,7 +87,7 @@ def test_link_new_repo_with_remote_refs_pull_failure(mocker): mocker.patch("pygitgo.commands.link.add_remote_origin") mocker.patch("pygitgo.commands.link.confirm_remote_link", return_value=True) mocker.patch("pygitgo.commands.link.get_current_branch", return_value="main") - mocker.patch("pygitgo.commands.link.get_config", return_value="main") + mocker.patch("pygitgo.commands.link.get_default_branch", return_value="main") fake_error = mocker.patch("pygitgo.commands.link.error") fake_warning = mocker.patch("pygitgo.commands.link.warning")