#!/usr/bin/env python3 """show dirty git repos""" from __future__ import division, print_function from colorama import Fore from pathlib import Path import contextlib import os import re import subprocess import click # TODO config file (and defaults here) IGNORES = [ r"\.sync", r"\.git/modules", # XXX check this again r"\.local/share/containers/storage", ] def git_directories(startdir) -> Path: for dirpath, dirnames, _ in os.walk(startdir): if any(re.search(ignore, dirpath) for ignore in IGNORES): continue if set(["info", "objects", "refs"]).issubset(set(dirnames)): yield Path(dirpath) @contextlib.contextmanager def working_directory(directory: Path): saved_cwd = os.getcwd() os.chdir(directory) yield os.chdir(saved_cwd) @click.command @click.argument( "topdirs", nargs=-1, type=click.Path(exists=True, file_okay=False, path_type=Path) ) @click.option( "-l", "--list", "list_", is_flag=True, default=False, help="Just list the dirty directories", ) def search_dirty(topdirs, list_): """Search for dirty git working directories in TOPDIRS""" if len(topdirs) == 0: topdirs = [Path(".")] for topdir in topdirs: for git_directory in git_directories(topdir): # technically, we could have a different GIT_DIR than ".git", but this script # assumes ".git". if git_directory.parts[-1] != ".git": continue work_tree_directory = git_directory.parent with working_directory(work_tree_directory): try: out = subprocess.check_output(['git', 'status', '-s'], stderr=subprocess.STDOUT) if len(out) > 0: if not list_: print( f"== {work_tree_directory}\n" f"{out.decode("utf-8")}" ) else: print(work_tree_directory) except subprocess.CalledProcessError as e: print( ( Fore.RED + "git status is unhappy with {}" + Fore.RESET ).format(work_tree_directory) ) print(e.output) if __name__ == "__main__": search_dirty()