An interactive Python script for managing multiple Git repository branch pulls and synchronization. Supports batch processing, parallel execution, automatic stashing, and comprehensive logging.
- Automatic Repository Discovery: Scans a directory tree for Git repositories
- Multi-Provider Support: Works with GitHub, GitLab, Bitbucket, and any Git provider
- Branch Management: Lists and manages multiple remote branches
- Interactive & Batch Modes: Choose between interactive selection or automated processing
- Persistent Interactive Session: After each operation, returns to the main menu until you quit with
q - Parallel Execution: Process multiple repositories concurrently for faster processing
- Safe Operations: Dry-run mode for testing before execution
- Local Changes Handling: Automatic stash/restore for repositories with uncommitted changes
- Flexible Remotes: Support for custom remote names (origin, upstream, etc.)
- Comprehensive Logging: Console output, file logging, and debug verbosity
- Comprehensive Test Suite: Unit tests, CLI tests, and edge case coverage
- Python 3.6+: Core requirement for running the script
- Git: Must be installed and accessible from command line
- click: CLI framework for argument parsing (automatically installed)
- pytest: Testing framework (optional, for running tests)
# Clone or download the repository
cd repomanager
# Create a virtual environment (recommended)
python -m venv .venv
# Activate the virtual environment
# On Windows:
.venv\Scripts\activate
# On Linux/macOS:
source .venv/bin/activate
# Install dependencies
pip install -r requirements.txt# Install only required dependencies
pip install clickRun the script from the directory containing your Git repositories:
# Interactive mode (default) - select repositories and branches manually
python repoManager.py
# Batch mode - process all repositories and branches automatically
python repoManager.py --all
# Dry-run simulation without making changes
python repoManager.py --all --dry-runIn interactive mode, the script does not exit after a single pull: it returns to the main repository menu so you can continue with other operations. Use q to exit.
Specify the root directory to scan for repositories (default: current directory)
# Scan repositories in a specific directory
python repoManager.py --root /path/to/repos
# Example with batch mode
python repoManager.py --root ~/projects --allSpecify the remote to use for fetch and pull operations (default: origin)
# Use upstream remote instead of origin
python repoManager.py --remote upstream
# Combine with batch mode
python repoManager.py --all --remote upstreamEnable batch/non-interactive mode. Processes all discovered repositories and their branches without prompting for user input.
# Process all repositories silently
python repoManager.py --all
# Useful for automated workflows
python repoManager.py --all --root /workspace/reposSimulate operations without executing destructive Git commands (checkout/pull). Safe for testing configurations and workflows.
# Preview what would happen without making changes
python repoManager.py --all --dry-run
# Dry-run with verbose output to see details
python repoManager.py --all --dry-run --verboseAutomatically answer "yes" to all interactive prompts. Useful when you want to proceed without confirmations.
# Skip confirmation prompts
python repoManager.py --yes
# Combine with local changes handling
python repoManager.py --all --yes --stashExecute repository processing in parallel using N worker threads (default: 1, sequential execution). In batch mode, each repository is handled independently to avoid concurrent operations on the same working tree.
# Process 4 repositories in parallel
python repoManager.py --all --parallel 4
# Maximum parallelism (careful with system resources)
python repoManager.py --all --parallel 8Note: Actual performance depends on network bandwidth and system resources.
Set timeout (seconds) for each git command before failing/retrying.
python repoManager.py --all --git-timeout 90Retry transient network git failures up to N times.
python repoManager.py --all --git-retries 2Write a structured JSON summary of every processed repository/branch operation.
python repoManager.py --all --summary-file logs/summary.jsonAutomatically handle uncommitted local changes by stashing them before checkout and restoring after pull. Ensures clean working directory for operations.
# Auto-stash before operations
python repoManager.py --all --stash
# Combine with parallel execution
python repoManager.py --all --stash --parallel 4How it works:
- Detects uncommitted changes with
git status --porcelain - Creates a stash with
git stash create - Performs checkout and pull
- Restores stash with
git stash pop
When used with --stash, preserves the created stash instead of automatically restoring it. Useful when you want to review changes manually.
# Stash changes but keep them for manual inspection
python repoManager.py --all --stash --stash-keep
# Combined with assume-yes for unattended execution
python repoManager.py --all --stash --stash-keep --yesSave all log output to a file in addition to console output. Useful for record-keeping and debugging.
# Save logs to file
python repoManager.py --all --log-file logs/pull.log
# Combine with verbose for detailed logging
python repoManager.py --all --verbose --log-file logs/debug.logFile locations are created automatically if they don't exist.
Enable verbose output with DEBUG-level logging. Shows detailed information about script operations.
# See detailed debug information
python repoManager.py --all --verbose
# Combine with file logging for full records
python repoManager.py --all --verbose --log-file logs/full.logpython repoManager.pyWhat happens:
- Scans current directory for repositories
- Shows list of found repositories
- Prompts user to select a repository
- Shows available remote branches
- Prompts user to select a branch
- Performs selected pull
- Returns to the main repository menu for more operations
- Exits only when user enters
q
python repoManager.py --allWhat happens:
- Scans current directory
- Pulls all branches from all repositories
- Uses 'origin' remote
- Processes sequentially (no parallelism)
python repoManager.py --all --parallel 4What happens:
- Scans current directory
- Processes up to 4 repositories in parallel
- Significantly faster for large numbers of repos
python repoManager.py --root ~/projects --all --remote upstreamWhat happens:
- Scans
~/projectsfor repositories - Processes all in batch mode
- Uses
upstreamremote instead oforigin
python repoManager.py --all --dry-run --verboseWhat happens:
- Scans repositories
- Shows what would be executed
- Doesn't perform any actual git operations
- Useful for testing configuration
python repoManager.py --all --stashWhat happens:
- Automatically stashes uncommitted changes
- Performs pull operations
- Restores stashed changes
- If stash pop conflicts occur, manual resolution is still required
python repoManager.py --all --stash --parallel 4 --log-file logs/sync.log --summary-file logs/summary.jsonWhat happens:
- Scans and pulls all repositories
- Handles local changes automatically
- Executes repository processing in parallel
- Saves detailed logs to
logs/sync.log - Saves structured results to
logs/summary.json
python repoManager.py --all --stash --stash-keep --yes --parallel 4 \
--git-timeout 90 --git-retries 2 \
--log-file logs/production.log --summary-file logs/summary.json --verboseWhat happens:
- Runs fully unattended (no prompts)
- Preserves stashed changes for review
- Fast parallel execution
- Comprehensive logging for auditing
- Structured output for automation/reporting
repomanager/
├── repoManager.py # Main script with GitRepoManager class
├── requirements.txt # Python dependencies
├── README.md # This documentation file
├── .gitignore # Git ignore rules
├── .github/
│ ├── workflows/ # CI/CD workflows
│ │ ├── ci.yml # Continuous integration tests
│ │ ├── workflow-main.yml # Production release workflow
│ │ ├── workflow-develop.yml # Development release workflow
│ │ └── workflow-dependabot-automerge.yml
│ └── dependabot.yml # Dependabot configuration
└── tests/ # Test suite
├── test_repo_manager.py # Unit tests for core functionality
├── test_cli.py # CLI argument tests
└── test_edge_cases.py # Edge cases and error scenarios
The project includes comprehensive tests covering:
- Unit tests: Core GitRepoManager functionality
- CLI tests: Command-line argument handling
- Edge cases: Error conditions and special scenarios
# Run all tests
pytest
# Run tests with verbose output
pytest -v
# Run tests with coverage report
pytest --cov=repoManager tests/
# Run specific test file
pytest tests/test_repo_manager.py
# Run specific test
pytest tests/test_repo_manager.py::test_find_git_repositories
# Run with full output and stop on first failure
pytest -vv -x# Generate and display coverage report
pytest --cov=repoManager --cov-report=html tests/
# Open coverage report in browser (after generating HTML)
# On Windows: start htmlcov/index.html
# On macOS: open htmlcov/index.html
# On Linux: xdg-open htmlcov/index.htmlWhen using --stash, conflicts may occur during git stash pop. Here's how to handle them:
If the git stash pop operation encounters conflicts:
- The stash remains saved - You won't lose any work
- The working tree shows conflicts - Git marks conflicted files with conflict markers
- The script reports an error - Details are logged for troubleshooting
- Manual resolution needed - You can resolve conflicts and retry
# 1. Check the conflicted files
git status
# 2. Edit conflicted files to resolve conflicts
# Look for markers like: <<<<<<, ======, >>>>>>
# 3. Stage the resolved files
git add <resolved-files>
# 4. Complete the stash pop
git stash drop
# 5. Commit the resolved changes
git commit -m "Resolved stash conflicts"- Use
--stash-keepto manually review changes before applying - Use
--dry-runfirst to preview what will happen - Consider using
--yeswith--stashonly when you're confident
Problem: Git is not installed or not in your system PATH
Solutions:
# Check if git is installed
git --version
# Install git from https://git-scm.com
# Then restart your terminalProblem: Running pytest from wrong directory
Solutions:
# Run pytest from repository root
cd /path/to/repomanager
pytest tests/
# Or run with Python module syntax
python -m pytest tests/Problem: Dependencies not installed
Solutions:
# Reinstall requirements
pip install -r requirements.txt
# Or install click directly
pip install click
# Verify installation
python -c "import click; print(click.__version__)"Problem: No write permissions to log directory
Solutions:
# Create logs directory with proper permissions
mkdir -p logs
chmod 755 logs
# Or specify log file in current directory
python repoManager.py --all --log-file pull.logProblem: Script doesn't find expected repositories
Solutions:
# Verify correct root directory
python repoManager.py --root /correct/path --all --dry-run
# Check directory structure
ls -la /path/to/repos
# Ensure .git directories exist
find /path -type d -name ".git" | head -5Problem: Script seems to hang or freeze
Solutions:
# Reduce parallel workers
python repoManager.py --all --parallel 2
# Add verbose output to see progress
python repoManager.py --all --verbose
# Try with a smaller subset
python repoManager.py --root /small/subset --all --verbose# Create a cron job (Linux/macOS)
0 9 * * * cd /path/to/repos && python /path/to/repoManager.py --all --stash \
--log-file /path/to/logs/daily.log 2>&1# Use in GitHub Actions
- name: Sync repositories
run: |
python repoManager.py --all --parallel 4 --stash \
--log-file sync.log --verbose# Create a wrapper script (sync-all.sh)
#!/bin/bash
for dir in ~/projects ~/work ~/personal; do
python repoManager.py --root "$dir" --all --parallel 4
done- Use
--parallelfor many repositories: Start with--parallel 4and adjust based on system resources - Use
--stashfor unattended operation: Automatically handles local changes - Monitor with
--log-fileand--summary-file: Keep records of operations for debugging and automation - Test with
--dry-runfirst: Before running on production repositories
- Requires Git to be accessible from command line
- Does not handle complex merge conflicts automatically (requires manual resolution)
- Parallel execution may stress network bandwidth with many repos
- Authentication must be configured in Git credentials
For bug reports, feature requests, or improvements:
- Test your changes locally:
pytest tests/ - Ensure code quality:
flake8 repoManager.py - Document changes in comments
- Submit pull requests with clear descriptions
MIT License - See LICENSE file for details
Last Updated: November 2025
Version: 1.0
Python Compatibility: 3.6+