3 min read

Git Automation with GitPython

This guide demonstrates how to automate common Git operations (clone, add, commit, push, pull) using the GitPython library. This is useful for build scripts, automated backups, or managing multiple repositories programmatically.

Modules Used:

  • git (GitPython): To interact with Git repositories.
  • argparse: To handle command-line arguments.

Installation

You need to install GitPython. You also need git installed on your system and available in your PATH.

pip install GitPython

The Code

Save this as git_tool.py.

import git
from git import Repo
import argparse
import os
import sys

def clone_repo(url, destination):
    if os.path.exists(destination):
        print(f"Error: Destination '{destination}' already exists.")
        return

    try:
        print(f"Cloning {url} to {destination}...")
        Repo.clone_from(url, destination)
        print("Clone successful.")
    except Exception as e:
        print(f"Clone failed: {e}")

def handle_repo(path, commit_msg=None, push=False, pull=False):
    try:
        repo = Repo(path)

        if repo.bare:
            print(f"Error: '{path}' is a bare repository.")
            return

        print(f"Repository: {path}")
        print(f"Active Branch: {repo.active_branch}")

        # 1. Pull
        if pull:
            print("Pulling changes from origin...")
            origin = repo.remotes.origin
            origin.pull()
            print("Pull complete.")

        # 2. Commit
        if commit_msg:
            # Check if there are changes (modified or untracked)
            if repo.is_dirty(untracked_files=True):
                print("Changes detected.")

                # Stage all files (git add .)
                print("Staging all files...")
                repo.git.add(A=True)

                # Commit
                print(f"Committing: {commit_msg}")
                repo.index.commit(commit_msg)
                print("Commit successful.")

                # 3. Push (only if committed)
                if push:
                    print("Pushing to origin...")
                    origin = repo.remotes.origin
                    origin.push()
                    print("Push successful.")
            else:
                print("No changes to commit.")
        elif push:
             # Push without commit if requested
             print("Pushing to origin...")
             origin = repo.remotes.origin
             origin.push()
             print("Push successful.")

    except git.exc.InvalidGitRepositoryError:
        print(f"Error: '{path}' is not a valid Git repository.")
    except Exception as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Git Automation Tool")
    subparsers = parser.add_subparsers(dest="command", help="Command to run")

    # Clone Command
    clone_parser = subparsers.add_parser("clone", help="Clone a repository")
    clone_parser.add_argument("url", help="Repository URL")
    clone_parser.add_argument("dest", help="Destination folder")

    # Sync Command (Commit/Push/Pull)
    sync_parser = subparsers.add_parser("sync", help="Sync a repository (Commit, Push, Pull)")
    sync_parser.add_argument("path", help="Path to local repository")
    sync_parser.add_argument("-m", "--message", help="Commit message (required for commit)")
    sync_parser.add_argument("--push", action="store_true", help="Push changes to remote")
    sync_parser.add_argument("--pull", action="store_true", help="Pull changes from remote before committing")

    args = parser.parse_args()

    if args.command == "clone":
        clone_repo(args.url, args.dest)
    elif args.command == "sync":
        handle_repo(args.path, args.message, args.push, args.pull)
    else:
        parser.print_help()

Usage

# Clone a repo
python git_tool.py clone https://github.com/gitpython-developers/GitPython.git ./my_repo

# Pull latest changes
python git_tool.py sync ./my_repo --pull

# Add all changes, commit, and push
python git_tool.py sync ./my_repo -m "Automated backup" --push

programming/python/python