Last active
July 1, 2021 19:25
-
-
Save tigerhawkvok/2161ef3aead495420fc84588ec50b6a3 to your computer and use it in GitHub Desktop.
Clean up a directory of Git projects
This file contains 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
#!python3 | |
""" | |
Cleanup all top level git directories here. | |
Creates a powershell or bash script to then execute | |
https://gist.github.com/tigerhawkvok/2161ef3aead495420fc84588ec50b6a3 | |
Python 3.7+ | |
""" | |
#pylint: disable= line-too-long, invalid-name | |
import os | |
import platform | |
import glob | |
import stat | |
# While convenient, unless you completely trust the | |
# directory names to not cause the shell script to be | |
# dangerously funky, leave this as False and do a quick | |
# sanity check first | |
EXECUTE_SCRIPT_AFTER_CREATION = False | |
# Cleans up aborted pack detritus. | |
# Do NOT set to True if you're cleaning on another process. | |
REMOVE_IN_PROGRESS_PACK_OBJECTS = True | |
# Remove stale LFS objects | |
CLEANUP_LFS = True | |
### No need to edit below here ### | |
fileExt = "ps1" if platform.system().lower() == "windows" else "sh" | |
feedbackStart = 'echo "`n' if platform.system().lower() == "windows" else r'printf "\n' | |
WRITE_FILE = f"gitCleanupScript.{fileExt}" | |
dirs = list() | |
removeList = list() | |
for maybeDir in glob.glob("*"): | |
# Check all directories at top level | |
if os.path.isdir(maybeDir) and (os.path.isdir(os.path.join(maybeDir, ".git")) or os.path.isfile(os.path.join(maybeDir, "HEAD"))): | |
# Only evaluate a directory if it has a ".git" folder | |
# or a "HEAD" file object (for bare repos) | |
dirs.append(maybeDir) | |
if REMOVE_IN_PROGRESS_PACK_OBJECTS: | |
# If there was a bad pack for whatever reason, clean them up | |
sub = ".git" if os.path.isdir(os.path.join(maybeDir, ".git")) else "." | |
searchDir = os.path.join(maybeDir, sub, "objects", "pack") | |
removed = 0 | |
for abortedPackFile in glob.glob(os.path.join(searchDir, ".tmp-*pack*")): | |
removeList.append(abortedPackFile) | |
removed += 1 | |
if removed > 0: | |
print(f"Going to remove {removed} abortive pack files from `{maybeDir}`...") | |
if len(dirs) == 0: | |
raise FileNotFoundError("No git directories found!") | |
failedRemove = 0 | |
for abortedPackFile in removeList: | |
try: | |
os.unlink(abortedPackFile) | |
except OSError: | |
try: | |
# In case of a permission error... | |
os.chmod(abortedPackFile, 0o0777) | |
os.unlink(abortedPackFile) | |
except OSError: | |
failedRemove += 1 | |
if failedRemove > 0: | |
print(f"Failed to remove {failedRemove} abortive pack files while enumerating directories") | |
if os.path.exists(WRITE_FILE): | |
os.unlink(WRITE_FILE) | |
lfsOp = "\ngit lfs prune" if CLEANUP_LFS else "" | |
for i, path in enumerate(dirs, 1): | |
opStr = f"cd '{path}'\ngit reflog expire --all --expire=now\ngit gc --prune=now --aggressive{lfsOp}\ncd ..\n" | |
with open(WRITE_FILE, "a") as fh: | |
if fileExt == "sh" and i == 1: | |
fh.write("#!/bin/sh\n") | |
fh.write(f'{feedbackStart}### Starting directory \'{path}\', {i} of {len(dirs)}... ###"\n') | |
fh.write(opStr) | |
fh.write("\n") | |
if fileExt == "sh": | |
with open(WRITE_FILE) as fd: | |
mode = os.fstat(fd.fileno()).st_mode | |
mode |= stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH | |
os.fchmod(fd.fileno(), stat.S_IMODE(mode)) | |
print(f"Ready to run {WRITE_FILE} ({len(dirs)} directories found)") | |
if EXECUTE_SCRIPT_AFTER_CREATION: | |
print("Executing...") | |
os.system(WRITE_FILE) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment