From 8fe3e1938580fb41af293fbf37b72f215f241023 Mon Sep 17 00:00:00 2001 From: Nathan McRae Date: Sun, 7 Jul 2024 20:06:28 -0700 Subject: [PATCH] Add check-ignore --- libwyag.py | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/libwyag.py b/libwyag.py index d9e106b..a208d02 100644 --- a/libwyag.py +++ b/libwyag.py @@ -957,3 +957,110 @@ def cmd_ls_files(args): print(f" uid: {e.uid} group: {e.gid}") print(f" flags: stage={e.flag_stage} assume_valid={e.flag_assume_valid}") +argsp = argsubparsers.add_parser("check-ignore", help="Check path(s) against ignore rules.") +argsp.add_argument("path", nargs="+", help="Paths to check") + +def cmd_check_ignore(args): + repo = repo_find() + rules = gitignore_read(repo) + for path in args.path: + if check_ignore(rules, path): + print(path) + +def gitignore_parse1(raw): + raw = raw.strip() + + if not raw or raw[0] == "#": + return None + elif raw[0] == "!": + return (raw[1:], False) + elif raw[0] == "\\": + return (raw[1:], True) + else: + return (raw, True) + +def gitignore_parse(lines): + ret = list() + + for line in lines: + parsed = gitignore_parse1(line) + if parsed: + ret.append(parsed) + + return ret + +class GitIgnore(object): + absolute = None + scoped = None + + def __init__(self, absolute, scoped): + self.absolute = absolute + self.scoped = scoped + +def gitignore_read(repo): + ret = GitIgnore(absolute=list(), scoped=dict()) + + # Read local configuration + repo_file = os.path.join(repo.gitdir, "info/exclude") + if os.path.exists(repo_file): + with open(repo_file, "r") as f: + ret.absolute.append(gitignore_parse(f.readlines())) + + # Global configuration + if "XDG_CONFIG_HOME" in os.environ: + config_home == os.environ["XDG_CONFIG_HOME"] + else: + config_home = os.path.expanduser("~/.config") + global_file = os.path.join(config_home, "git/ignore") + + if os.path.exists(global_file): + with open(global_file, "r") as f: + ret.absolute.append(gitignore_parse(f.readlines())) + + # .gitignore files in the index + index = index_read(repo) + + for entry in index.entries: + if entry.name == ".gitignore" or entry.name.endswith("/.gitignore"): + dir_name = os.path.dirname(entry.name) + contents = object_read(repo, entry.sha) + lines = contents.blobdata.decode("utf8").splitlines() + ret.scoped[dirname] = gitignore_parse(lines) + return ret + +def check_ignore1(rules, path): + result = None + for (pattern, value) in rules: + if fnmatch(path, pattern): + result = value + return result + +def check_ignore_scoped(rules, path): + parent = os.path.dirname(path) + while True: + if parent in rules: + result = check_ignore1(rules[parent], path) + if result != None: + return result + if parent == "": + break + parent = os.path.dirname(parent) + return None + +def check_ignore_absolute(rules, path): + parent = os.path.dirname(path) + for ruleset in rules: + result = check_ignore1(ruleset, path) + if result != None: + return result + return False + +def check_ignore(rules, path): + if os.path.isabs(path): + raise Exception("This function requires path to be relative to the repository's root") + + result = check_ignore_scoped(rules.scoped, path) + if result != None: + return result + + return check_ignore_absolute(rules.absolute, path)