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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ Versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- **Auto SSH Commit Signing:** GitGo now uses the SSH key generated during `gitgo user login` to automatically sign all commits using temporary `-c` flags, giving you the Verified badge on GitHub without modifying global git configs.

### Changed
- Refactored CLI messaging to use a consistent tone, removing robotic phrases and filler words.
- Standardized terminal output spacing and colors across all commands.
- Updated success banners to a concise, military-style format for major operations.
- Improved error messages to provide actionable next steps instead of generic advice.
- Refactored codebase to standardize internal API returns, remove redundant checks, and optimize stash operations.

### Fixed
Expand Down
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.0b3"
version = "1.7.0b4"
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
53 changes: 27 additions & 26 deletions src/pygitgo/commands/git_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
def get_status_content():
status = run_command(["git", "status", "--porcelain"], allow_fail=True)
if command_failed(status) or not status.strip():
raise GitGoError("\nWorking tree is clean. Nothing to commit.\n")
raise GitGoError("Working tree is clean. Nothing to commit.")
return status

def get_current_branch():
Expand All @@ -25,7 +25,7 @@ def get_main_branch():
default_main_branch = get_config("default-branch", "main")
if command_failed(main_branch):
return default_main_branch

return main_branch.split("HEAD branch:")[-1].strip().splitlines()[0].strip() if "HEAD branch:" in main_branch else default_main_branch

def is_branch_exist(branch):
Expand All @@ -41,9 +41,10 @@ def git_new_branch(branch):
from pygitgo.commands.jump import jump_operation
jump_operation(Namespace(branch=branch))
else:
raise GitGoError(f"\nOperation canceled. Branch '{branch}' already exists.\n")
raise GitGoError(f"Operation canceled. Branch '{branch}' already exists.")
else:
success(f"\nBranch '{branch}' created.\n")
print()
success(f"Branch '{branch}' created.")

return branch

Expand All @@ -64,7 +65,7 @@ def git_commit(commit_message, loading_msg="Commiting changes...", skip_staging=

if not skip_staging:
run_command(["git", "add", "."], loading_msg="Staging files...")

clean_message = commit_message.strip('"\'')

signing_flags = _get_signing_flags()
Expand All @@ -78,7 +79,7 @@ def git_init():
if os.path.isdir(".git"):
warning("Already a git repository! Skipping init...")
return False

default_main_branch = get_config("default-branch", "main")

result = run_command(["git", "init", "-b", default_main_branch], allow_fail=True, loading_msg="Initializing git repository...")
Expand All @@ -92,32 +93,32 @@ def git_init():

def add_remote_origin(repo_url):
clean_url = repo_url.strip('"\'')

existing_remote = run_command(["git", "remote", "get-url", "origin"], allow_fail=True)
if not command_failed(existing_remote):
warning(f"Remote origin already exists: {existing_remote}")
run_command(["git", "remote", "set-url", "origin", clean_url], loading_msg="Updating remote URL...")
else:
run_command(["git", "remote", "add", "origin", clean_url], loading_msg="Adding remote origin...")

success(f"Remote origin set to: {clean_url}")


def confirm_remote_link():
test_result = run_command(["git", "ls-remote", "origin"], allow_fail=True, loading_msg="Testing connection to remote...")

if command_failed(test_result):
error("Failed to connect to remote repository!")
warning("Please check your repository URL and network connection.")
error("Connection failed — verify the URL and your SSH key.")
info("Run: git remote -v to inspect your current remote.")
return False
success("Successfully connected to remote repository.")

success("Remote is reachable.")
return True


def create_main_branch():
current_branch = run_command(["git", "branch", "--show-current"], allow_fail=True)

if command_failed(current_branch) or not current_branch.strip():
run_command(["git", "checkout", "-b", "main"], loading_msg="Setting default branch to 'main'...")
elif current_branch.strip() != "main":
Expand All @@ -143,9 +144,9 @@ def check_and_sync_branch(branch):
output = run_command(["git", "pull", "--rebase", "origin", branch], loading_msg="Pulling changes from remote...")
if output:
print(output)
success("Successfully synced with remote!")
success("Synced with remote.")
else:
success("Branch is up to date or ahead of remote.")
success("Branch is up to date.")
else:
success("Branch is already up to date.")
except (GitCommandError, ValueError):
Expand All @@ -156,25 +157,25 @@ def check_and_sync_branch(branch):

def git_push(branch):
remote_url = run_command(["git", "remote", "get-url", "origin"], allow_fail=True)

if not command_failed(remote_url) and remote_url:
remote_url = remote_url.strip()

if not is_ssh_url(remote_url) and check_connection():
ssh_url = convert_https_to_ssh(remote_url)
if ssh_url:
run_command(["git", "remote", "set-url", "origin", ssh_url], loading_msg="Converting remote from HTTPS to SSH for secure push...")
success(f"Remote updated to: {ssh_url}")

try:
try:
run_command(["git", "push", "-u", "origin", branch], loading_msg=f"Pushing to remote branch '{branch}'...")
except (GitCommandError, OSError) as e:
error("Failed to push to remote repository!")
warning("Please check your network connection, remote URL, and authentication.")
error("Push failed — verify your remote URL and SSH key, then try again.")
info("Run: git remote -v to inspect your current remote.")
if "rebase in progress" in str(e):
handle_rebase()
else:
raise GitGoError()
raise GitGoError("Push failed — see above.")


def handle_rebase():
Expand All @@ -183,11 +184,11 @@ def handle_rebase():
return False

if "rebase in progress" in status or "rebase" in status.lower():
warning("\nConflict detected!")
warning("Please resolve conflicts manually, then run:")
warning("Conflict detected during rebase.")
info("Resolve conflicts manually, then run:")
info(" git add <files>")
info(" git rebase --continue")
warning("When finished, run 'gitgo push <branch> <message>' again.\n")
raise GitGoError()
info("When finished, run 'gitgo push <branch> <message>' again.")
raise GitGoError("Push aborted — rebase conflict in progress.")

return True
81 changes: 44 additions & 37 deletions src/pygitgo/commands/jump.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,97 +21,104 @@ def undo_jump_operation(original_branch, stashed_code, created_branch=None):
if stashed_code:
pop_result = git_stash_pop()
if not pop_result:
warning("\nCould not restore your unsaved changes automatically. Run 'gitgo state list' to recover them.\n")
warning("Could not restore your unsaved changes automatically. Run 'gitgo state list' to recover them.")

success(f"\nCanceled safely!")
success(f"You are back on your original branch '{original_branch}', and your code is totally safe.\n")
print()
success(f"Canceled safely.")
success(f"Back on '{original_branch}'. Your code is safe.")

def jump_operation(args):

def jump_operation(args):

target_branch = args.branch

original_branch = get_current_branch()

if original_branch == target_branch:
warning(f"\nYou are already on branch '{target_branch}'.\n")
warning(f"Already on branch '{target_branch}'.")
return

has_changes = run_command(['git', 'status', '--porcelain'], allow_fail=True, loading_msg="Checking for uncommitted changes...")
if command_failed(has_changes):
raise GitGoError("\nUnable to check for uncommitted changes. Please ensure you're in a valid git repository.\n")
raise GitGoError("Unable to check for uncommitted changes — make sure you're in a valid git repository.")

stashed_code = False
if has_changes.strip():
info("\nYou have unsaved changes here.")
print()
info("You have unsaved changes here.")
user_input = input("Do you want to move these changes to your new branch? (y/n): ").strip().lower()
if user_input != 'y':
warning("\nYou cannot switch branches with unsaved changes. Jump canceled.\n")
print()
warning("You cannot switch branches with unsaved changes. Jump canceled.")
return
else:
stash_result = git_stash_push(label="GitGo Jump Auto-Stash", loading_msg="Saving your changes before jumping...")
if not stash_result:
warning("\nFailed to save your changes. Please resolve any issues and try again.")
raise GitGoError()
info("\nYour changes have been saved. Jumping to the new branch...")
warning("Stash failed. Your working tree may have untracked files.")
info("Run: git status to see what's blocking the stash.")
raise GitGoError("Jump aborted — could not save working changes.")
info("Changes saved. Jumping to the new branch...")
stashed_code = True

created_branch = None
if not is_branch_exist(target_branch):
warning(f"\nBranch '{target_branch}' does not exist.\n")
print()
warning(f"Branch '{target_branch}' does not exist.")
user_input = input("Do you want to create it and jump to it? (y/n): ").strip().lower()

if user_input != 'y':
info("Exiting without jumping...\n")
info("Exiting without jumping...")
if stashed_code:
pop_result = git_stash_pop(loading_msg="Putting your unsaved changes back...")
if not pop_result:
warning("\nCould not restore your unsaved changes automatically. Run 'gitgo state list' to recover them.")
warning("Could not restore your unsaved changes automatically. Run 'gitgo state list' to recover them.")
return

git_new_branch(target_branch)
created_branch = target_branch
else:
run_command(['git', 'checkout', target_branch], loading_msg=f"Moving you to branch '{target_branch}'...")

main_branch = get_main_branch()
get_origin_updates = run_command(['git', 'pull', 'origin', main_branch], allow_fail=True, loading_msg=f"Downloading the latest updates from '{main_branch}'...")

if command_failed(get_origin_updates):
warning(f"\nFailed to pull updates from '{main_branch}'. Make sure you have internet or the remote branch exists.")
user_input = input("Do you want to stay on the new branch without the latest updates? (y/n): ").strip().lower()
warning(f"Failed to pull updates from '{main_branch}'. No internet, or the remote branch doesn't exist yet.")
user_input = input("Stay on the new branch without the latest updates? (y/n): ").strip().lower()
if user_input != 'y':
undo_jump_operation(original_branch, stashed_code, created_branch)
raise GitGoError()
raise GitGoError("Jump aborted — could not sync with remote.")
else:
success(f"\nOkay! You are on the new branch, but without the latest updates from '{main_branch}'.")

info(f"On '{target_branch}', but without the latest updates from '{main_branch}'.")

if stashed_code:
apply_result = git_stash_apply(loading_msg="Unpacking your unsaved changes...")
if not apply_result:
error("\nSTOP! There is a 'Merge Conflict'.")
warning("Your unsaved code clashes with the new code from 'main'.\n")
info("Option [Y]: Stay here and fix the red conflict lines yourself.")
info("Option [N]: Cancel everything and go back to normal.\n")
print()
error("MERGE CONFLICT — your changes clash with the target branch.")
print()
info("Option [Y]: Stay here and fix the conflict lines yourself.")
info("Option [N]: Cancel everything and go back to where you started.")
print()
conflict_choice = input("Do you want to fix it yourself? (y/n): ").strip().lower()

if conflict_choice != 'y':
undo_jump_operation(original_branch, stashed_code, created_branch)
return
else:
success("\nOkay! You are on the new branch with your code.")
warning("Please open your code editor RIGHT NOW to fix the conflicts!")
info("Your stash backup is still saved. Run 'gitgo state list' to see it.\n")
print()
success(f"On '{target_branch}'. Conflict markers are in your files.")
warning("Open your editor and fix the conflict lines.")
info("Your stash backup is still saved. Run 'gitgo state list' to see it.")
return
else:
drop_result = git_stash_drop(loading_msg="Cleaning up the temporary stash...")
if not drop_result:
warning("\nCould not clean up the temporary stash. Run 'gitgo state list' to remove it manually.")
success(f"\nSuccess! You are now on '{target_branch}'.")
success("Your unsaved code was moved here safely!\n")
warning("Could not clean up the temporary stash. Run 'gitgo state list' to remove it manually.")
print()
success(f"On '{target_branch}'. Your changes came with you.")
return
else:
success(f"\nSuccess! You are now on '{target_branch}'.\n")
return

print()
success(f"On '{target_branch}'.")
return
33 changes: 15 additions & 18 deletions src/pygitgo/commands/pull.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,32 @@ def pull_operation(args):

if not branch:
branch = get_current_branch()
info(f"No branch provided. Pulling latest updates for '{branch}'...\n")
info(f"No branch provided. Pulling latest updates for '{branch}'...")

try:
remote_refs = run_command(["git", "ls-remote", "--heads", "origin", branch], allow_fail=True)
if command_failed(remote_refs) or not remote_refs.strip():
error(f"\nFailed! The branch '{branch}' does not exist on the remote server.")
warning("You might need to push your local branch first.\n")
raise GitGoError()
error(f"Branch '{branch}' does not exist on the remote.")
info("Push your local branch first, or verify the branch name.")
raise GitGoError("Pull aborted — branch not found on remote.")

run_command(
["git", "pull", "--rebase", "--autostash", "origin", branch],
["git", "pull", "--rebase", "--autostash", "origin", branch],
loading_msg=f"Downloading latest updates for '{branch}' (auto-saving your code)..."
)
success(f"\nSuccess! Your project is now up to date with '{branch}'.\n")

success(f"Project is up to date with '{branch}'.")

except GitCommandError as e:
error_msg = str(e).lower()
if "conflict" in error_msg or "rebase in progress" in error_msg:
error("\nMERGE CONFLICT DETECTED!")
warning("Your local code clashes with the new code downloaded from the server.\n")
info("1. Open your code editor down below.")
info("2. Fix the red conflict lines in your files.")
error("MERGE CONFLICT DETECTED!")
print()
info("1. Open your code editor.")
info("2. Fix the conflict lines in your files.")
info("3. Save the files.")
info("4. Run this command to finish: git rebase --continue\n")
raise GitGoError()
info("4. Run: git rebase --continue")
print()
raise GitGoError("Pull failed — resolve conflicts to continue.")
else:
raise GitGoError(
"\nFailed to pull updates from the server!"
"Please check your internet connection and try again."
f"Details: {e}\n"
)
raise GitGoError(f"Failed to pull from remote: {e}")
Loading
Loading