Boost Your Git Workflow With Python and Bash

Managing multiple local Git repositories can be time-consuming and error-prone (ever forget a commit?). To streamline this process, I’ve created a set of Python scripts designed to automate some common Git operations. These scripts offer a powerful solution for efficiently managing your local Git repositories. By automating tasks like checking for local changes and pulling updates from remote repositories, you can significantly boost your productivity and reduce the risk of errors.

In this post, I’ll walk you through the scripts, explain their functionality, and provide step-by-step instructions on how to set them up and use them effectively.

These are some of my favorite helper scripts. Let’s dive in!

Table of Contents

What You’ll Need

  • Linux environment (I’m using Ubuntu)
  • local git repos (ie, in ~/git)
  • Bash and Python3

Source Code

Download the source code from vicsprotips on GitHub!

Let’s Get Started!

I have these scripts split into two concepts — “git pull” and “check local git for modifications”.

Let’s review the source code for each before we proceed.

check-git.py

The check-git.py script takes a target directory (ie, ~/git) and checks every local git repo under the target for any local changes. The script outputs a list of local repos that have content modifications. This script is extremely useful for identifying any outstanding commits that need to be made across one-to-many local repositories.

# https://vicsprotips.com
# check git: search for .git folders from given root and notify you if you have changes to commit
# usage: python3 check-git.py ~/git
# Vic Baker

import os, sys, subprocess

queue = []
print(f"Checking local repos under {sys.argv[1]}")
for root, dirs, files in os.walk(sys.argv[1], topdown=False):
    for d in dirs:
        if d == '.git':
            print(f"{root}")
            cmd = f'cd {root}; git -c color.status=always status'
            returned_output = subprocess.check_output(cmd,shell=True).decode('utf-8')
            if 'nothing to commit' not in returned_output:
                queue.append(root)
                print(f'{root}\n{returned_output}')

print('\n\nSummary:')
for q in queue:
    print(q)

mp-git-pull.py

The mp-git-pull.py script checks the remote for every local git repo under a specified target (ie, ~/git) and performs a git pull on each. This script utilizes python multiprocessing to perform the tasks in parallel for a significant performance boost over the single process alternative (see git-pull.py)

In summary, the script below takes a target dir as the argument (ie, ~/git) and builds a queue of local repos found under the target. The queue of local repo paths is then sent to a multiprocessing pool (in this case, the pool size will default to the number of cores), and perform a git pull for each local repo. This script is fantastic when dealing with many repositories frequently being updated (ie, think CI/CD or working with teams of developers across multiple repos).

# https://vicsprotips.com
# git fetch (multi-process): search for .git folders from given root and pull origin for each
# much faster than non-mp version
# usage: python3 gf.py ~/git
# Vic Baker

import multiprocessing
import subprocess

import os,sys
import requests

def doWork(params):
    # check provided dir for git updates
    try:
        dir = params
        # print(f"processing: {dir}")
        cmd = f'cd {dir}; git -c color.status=always pull'
        returned_output = subprocess.check_output(cmd,shell=True).decode('utf-8')
        return f"{dir}:{returned_output}".strip()
    except Exception as e:
        return f"{dir}: unable to pull -- local changes?".strip()

if __name__ == '__main__':
    rootDir=sys.argv[1]
    
    import os, sys, subprocess
    queue = []
    print(f"Generating queue for {rootDir}")
    for root, dirs, files in os.walk(rootDir, topdown=True):
        if ".git" in dirs:
            queue.append(root)

    print("Queue created.  Starting pool")
    
    pool = multiprocessing.Pool()
    results = pool.map(doWork, queue)
    pool.close()
    print("Subprocess pools done!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
    c = 0
    updates = []
    for r in results:
        if not r.endswith("Already up to date."):
            c = c + 1
            updates.append(r.split(':', 1)[0])
            print(r)
    if len(updates) > 0:
        print("\n\nThe following repos had updates:")
        for r in updates:
            print(r)


    

Next, we’ll walk through setting them up for you to use in your Linux environment via cloning and creating aliases.

Cloning the scripts

Clone the scripts from vicsprotips GitHub and put them in your ~/git folder. I’ve put mine under ~/git/vicsprotips/git-bash-utils. It should look something like this:

Adding aliases to ~/.bashrc

Next, we need to add the following aliases to your ~/.bashrc file so that the aliases will be active every time you start a bash terminal.

# add the following to your ~/.bashrc
alias gp="python3 ~/git/vicsprotips/git-bash-utils/git-pull/mp-git-pull.py ~/git"
alias cg="python3 ~/git/vicsprotips/git-bash-utils/check-git/check-git.py ~/git"

Save the changes to your ~/.bashrc file. You can now either close and reopen each terminal window, or, from within each bash terminal type source ~/.bashrc which will activate the aliases in the current terminal.

Note: You can call the gp or cg aliases from anywhere and they’ll check EVERY git repo under your target (~/git). They are not dependent upon which directory you’re in when they’re called. The scripts work from the ~/git target we set in the alias — adjust that for your use case if different.

Using the git-bash-utils

cg — ‘check git’

From your bash terminal window type cg to check your local repos for any outstanding local modifications that have been made to your git repos. The script will output a summary of repos with local changes. As shown below, the script scans local repos under the target set in the ~/.bashrc alias and detects that I have an outstanding commit to the check-git.py script (I added a comment at the top of the modified file).

The summary section shows a full path for the repo(s) with local changes. You can then handle each local repo modification via your regular git processes (ie, git commit, etc) NOTE: The cg script only alerts you of local changes detected.

gp — ‘git pull’

Now let’s see the gp alias in action. In our alias, we set ~/git as the target for the script to work with and check the remotes for every local repo under our ~/git folder. The output below shows that the script detected and pulled a remote change.

In Closing

We’ve walked through an approach to getting the scripts setup in your local environment. Someday I might write a convenience script but I think it’s worthwhile for us to walk through things together. Hopefully, you’ve learned something and you find these scripts as useful as I do.

Scroll to Top