Skip to content
Merged
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Binary file modified assets/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
Expand Down
6 changes: 3 additions & 3 deletions src/pygitgo/commands/git_core.py
Original file line number Diff line number Diff line change
@@ -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


Expand Down Expand Up @@ -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...")
Expand Down
14 changes: 4 additions & 10 deletions src/pygitgo/commands/link.py
Original file line number Diff line number Diff line change
@@ -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


Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}'...")
Expand All @@ -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.")
Expand Down
7 changes: 5 additions & 2 deletions src/pygitgo/main.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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:
Expand Down
73 changes: 44 additions & 29 deletions src/pygitgo/utils/config.py
Original file line number Diff line number Diff line change
@@ -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


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")

49 changes: 48 additions & 1 deletion tests/test_config_utils.py
Original file line number Diff line number Diff line change
@@ -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


Expand Down Expand Up @@ -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"
4 changes: 2 additions & 2 deletions tests/test_git_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand All @@ -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(
Expand Down
8 changes: 4 additions & 4 deletions tests/test_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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")

Expand Down Expand Up @@ -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")

Expand Down
Loading