Last active
April 1, 2025 07:47
-
-
Save Ap0dexMe0/33f435c4e501f8019d15b0fdbb360658 to your computer and use it in GitHub Desktop.
Git Manager CLI: stage modified/untracked files, commit and push with interactive selection and colored output.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| import os | |
| import sys | |
| import subprocess | |
| COLORS = { | |
| 'blue': '\033[1;34m', | |
| 'green': '\033[1;32m', | |
| 'red': '\033[1;31m', | |
| 'purple': '\033[1;35m', | |
| 'cyan': '\033[0;36m', | |
| 'yellow': '\033[1;33m', | |
| 'bold': '\033[1m', | |
| 'reset': '\033[0m' | |
| } | |
| def color_text(text, color): | |
| return f"{COLORS.get(color, '')}{text}{COLORS['reset']}" | |
| def frame_title(title): | |
| line = "═" * (len(title) + 3) | |
| print(color_text(f"\n╔{line}╗", 'blue')) | |
| print(color_text(f"║ {title} ║", 'blue')) | |
| print(color_text(f"╚{line}╝\n", 'blue')) | |
| def run_git_command(cmd): | |
| try: | |
| result = subprocess.run(['git'] + cmd, | |
| check=True, | |
| stdout=subprocess.PIPE, | |
| stderr=subprocess.PIPE, | |
| text=True) | |
| return result.stdout.strip() | |
| except subprocess.CalledProcessError as e: | |
| print(color_text(f"\n❌ Git command failed: {' '.join(cmd)}", 'red')) | |
| print(color_text(e.stderr.strip(), 'red')) | |
| sys.exit(1) | |
| def show_detailed_git_status(): | |
| frame_title("🔍 Git Status") | |
| status_output = run_git_command(['status', '--short', '--branch']) | |
| has_output = False | |
| for line in status_output.splitlines(): | |
| if line.startswith('##'): | |
| continue # Skip branch info | |
| if line.startswith('??'): | |
| print(f"🆕 {color_text('[untracked]', 'purple')} {line[3:]}") | |
| has_output = True | |
| elif line.startswith(' M'): | |
| print(f"✏️ {color_text('[modified]', 'red')} {line[3:]}") | |
| has_output = True | |
| elif line.startswith('A ') or line.startswith('M '): | |
| print(f"✅ {color_text('[staged]', 'green')} {line[3:]}") | |
| has_output = True | |
| if not has_output: | |
| print(color_text("✅ Working directory clean.", 'green')) | |
| print(color_text("─" * 60, 'blue')) | |
| def select_files(): | |
| staged = run_git_command(['diff', '--name-only', '--cached']).splitlines() | |
| unstaged = run_git_command(['diff', '--name-only']).splitlines() | |
| untracked = run_git_command(['ls-files', '--others', '--exclude-standard']).splitlines() | |
| staged_set = set(staged) | |
| modified_files = [f for f in unstaged if f and f not in staged_set] | |
| untracked_files = [f for f in untracked if f and f not in staged_set] | |
| choices = [] | |
| for f in modified_files: | |
| choices.append(('modified', f)) | |
| for f in untracked_files: | |
| choices.append(('untracked', f)) | |
| if not choices: | |
| print(color_text("⚠️ No unstaged or untracked files to select.", 'red')) | |
| sys.exit(0) | |
| frame_title("📁 Select Files to Stage") | |
| for i, (status, file) in enumerate(choices, 1): | |
| emoji = "✏️" if status == 'modified' else "🆕" | |
| status_color = 'red' if status == 'modified' else 'purple' | |
| print(f" {color_text(str(i).rjust(2), 'cyan')}. {emoji} {color_text(f'[{status}]', status_color)} {file}") | |
| print() | |
| print(color_text("Options:", 'yellow')) | |
| print(f" {color_text('0', 'cyan')} - ➕ Stage all listed files") | |
| print(f" {color_text('q', 'cyan')} - ❌ Quit without staging") | |
| selections = input(color_text("\nYour selection ➜ ", 'yellow')).strip() | |
| if selections.lower() == 'q': | |
| print(color_text("❌ Aborted by user.", 'yellow')) | |
| sys.exit(0) | |
| selected_files = [] | |
| if selections == '0': | |
| selected_files = [file for _, file in choices] | |
| else: | |
| try: | |
| indices = list(map(int, selections.split())) | |
| for index in indices: | |
| if index < 1 or index > len(choices): | |
| print(color_text(f"❌ Invalid selection: {index}", 'red')) | |
| sys.exit(1) | |
| selected_files.append(choices[index - 1][1]) | |
| except ValueError: | |
| print(color_text("❌ Invalid input. Use numbers separated by spaces.", 'red')) | |
| sys.exit(1) | |
| print() | |
| for file in selected_files: | |
| run_git_command(['add', file]) | |
| print(color_text(f"✔️ Staged: {file}", 'green')) | |
| print(color_text("─" * 60, 'blue')) | |
| def main(): | |
| os.system('cls' if os.name == 'nt' else 'clear') | |
| frame_title("🚀 Git Manager CLI") | |
| show_detailed_git_status() | |
| select_files() | |
| print() | |
| message = input(color_text("📝 Commit message (default: released): ", 'yellow')).strip() | |
| if not message: | |
| message = "released" | |
| print() | |
| print(color_text(f"📦 Committing: '{message}'", 'blue')) | |
| run_git_command(['commit', '-m', message]) | |
| print(color_text("📤 Pushing to remote...", 'blue')) | |
| run_git_command(['push']) | |
| print(color_text("\n✅ All done! Your changes have been committed and pushed.", 'green')) | |
| print(color_text("─" * 60, 'blue')) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment