Skip to content

Instantly share code, notes, and snippets.

@dhondta
Last active January 17, 2026 12:14
Show Gist options
  • Select an option

  • Save dhondta/33992b6b2c68563b698baded4736ecf1 to your computer and use it in GitHub Desktop.

Select an option

Save dhondta/33992b6b2c68563b698baded4736ecf1 to your computer and use it in GitHub Desktop.

Git Permissions Recovery

This is a small tool using Tinyscript to restore permissions under a Git repository (e.g. when a bad backup and restore method messed up with file permissions).

$ pip install pybots tinyscript
$ tsm install git-perm-recovery
#!/usr/bin/env python
from tinyscript import *
__author__ = "Alexandre D'Hondt"
__version__ = "1.0"
__copyright__ = ("A. D'Hondt", 2025)
__examples__ = ["", "-r"]
__doc__ = """
This is a simple tool for restoring permissions under a Git repository
(e.g. when a bad backup and restore method messed up with file permissions).
"""
REGEX = re.compile(r"^ mode change 100(\d+) => 100\d+ (.*)$")
def git_recover_perms():
""" This will revert permissions back to normal in the current folder. """
# first, check that the given path is a Git repo
out, err, retc = ts.execute("git diff --summary", returncode=True)
if retc > 0:
err = err.decode().split(".")[0].strip()
if err == "warning: Not a git repository":
raise OSError(f"'{Path.cwd()}' is not a Git repository")
elif err == "fatal: this operation must be run in a work tree":
raise OSError("Not the root of the Git repository")
raise Exception(err)
# then check that it is the root
p, _ = ts.execute("git rev-parse --show-toplevel")
if ts.Path(".").absolute().parts != ts.Path(p.decode().strip()).parts:
raise OSError("Not the root of the Git repository")
# now fix permissions
fixed = False
for line in out.decode().splitlines():
if (m := REGEX.search(line)):
old_perm, path = m.groups()
ts.Path(path).chmod(int(old_perm, 8))
fixed = True
return fixed
if __name__ == '__main__':
parser.add_argument("-r", "--recurse", action="store_true", help="recurse for Git repositories from here")
initialize()
try:
ts.execute("git --help")
logger.debug("'git' is installed")
except FileNotFoundError:
logger.critical("'git' is not installed")
sys.exit(1)
cwd = ts.Path.cwd()
for path in (cwd.walk(filter_func=lambda p: p.is_dir()) if args.recurse else [cwd]):
os.chdir(str(path))
try:
logger.debug(f"fixed permissions for '{path}'") if git_recover_perms() else \
[logger.warning, logger.debug][args.recurse](f"nothing to change in '{path}'")
except OSError as e:
if not args.recurse:
logger.error(e)
except Exception as e:
logger.error(f"{path}: {e}")
os.chdir(str(cwd))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment